mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-14 20:17:14 +01:00
Merge pull request #52 from exciler/subscriptioncycle
Subscriptioncycle
This commit is contained in:
commit
f02be869d0
@ -1,10 +1,17 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-FileCopyrightText: 2019 Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\SubscriptionCycle;
|
||||
|
||||
use Aboabrechnung;
|
||||
use Sabre\CalDAV\Subscriptions\Subscription;
|
||||
use Xentral\Components\SchemaCreator\Collection\SchemaCollection;
|
||||
use Xentral\Components\SchemaCreator\Schema\TableSchema;
|
||||
use Xentral\Components\SchemaCreator\Type;
|
||||
@ -35,6 +42,7 @@ final class Bootstrap
|
||||
'SubscriptionCycleJobService' => 'onInitSubscriptionCycleJobService',
|
||||
'SubscriptionCycleFullTask' => 'onInitSubscriptionCycleFullTask',
|
||||
'TaskMutexService' => 'onInitTaskMutexService',
|
||||
'SubscriptionModule' => 'onInitSubscriptionModule'
|
||||
];
|
||||
}
|
||||
|
||||
@ -60,20 +68,12 @@ final class Bootstrap
|
||||
*/
|
||||
public static function onInitSubscriptionCycleManualJobTask(ContainerInterface $container
|
||||
): SubscriptionCycleManualJobTask {
|
||||
$legacyApp = $container->get('LegacyApplication');
|
||||
|
||||
$subscriptionCycleModule = $legacyApp->loadModule('rechnungslauf');
|
||||
$subscriptionModule = new Aboabrechnung($legacyApp);
|
||||
$subscriptionModule->cronjob = true;
|
||||
|
||||
return new SubscriptionCycleManualJobTask(
|
||||
$legacyApp,
|
||||
$container->get('LegacyApplication'),
|
||||
$container->get('Database'),
|
||||
$container->get('TaskMutexService'),
|
||||
$container->get('SubscriptionCycleJobService'),
|
||||
$subscriptionCycleModule,
|
||||
$subscriptionModule,
|
||||
!empty($legacyApp->erp->GetKonfiguration('rechnungslauf_gruppen'))
|
||||
$container->get('SubscriptionModule')
|
||||
);
|
||||
}
|
||||
|
||||
@ -210,22 +210,10 @@ final class Bootstrap
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SchemaCollection $collection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function registerTableSchemas(SchemaCollection $collection): void
|
||||
{
|
||||
$subscriptionCycleJob = new TableSchema('subscription_cycle_job');
|
||||
$subscriptionCycleJob->addColumn(Type\Integer::asAutoIncrement('id'));
|
||||
$subscriptionCycleJob->addColumn(new Type\Integer('address_id'));
|
||||
$subscriptionCycleJob->addColumn(new Type\Varchar('document_type', 32));
|
||||
$subscriptionCycleJob->addColumn(new Type\Varchar('job_type', 32));
|
||||
$subscriptionCycleJob->addColumn(new Type\Integer('printer_id'));
|
||||
$subscriptionCycleJob->addColumn(new Type\Timestamp('created_at', 'CURRENT_TIMESTAMP'));
|
||||
$subscriptionCycleJob->addIndex(new Index\Primary(['id']));
|
||||
$subscriptionCycleJob->addIndex(new Index\Index(['address_id']));
|
||||
$collection->add($subscriptionCycleJob);
|
||||
public static function onInitSubscriptionModule(ContainerInterface $container): SubscriptionModule {
|
||||
return new SubscriptionModule(
|
||||
$container->get('LegacyApplication'),
|
||||
$container->get('Database')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-FileCopyrightText: 2019 Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
@ -16,52 +21,33 @@ use Xentral\Modules\SubscriptionCycle\SubscriptionModuleInterface;
|
||||
|
||||
final class SubscriptionCycleManualJobTask
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
private $app;
|
||||
private ApplicationCore $app;
|
||||
private Database $db;
|
||||
private SubscriptionCycleJobService $cycleJobService;
|
||||
private TaskMutexServiceInterface $taskMutexService;
|
||||
private SubscriptionModuleInterface $subscriptionModule;
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var SubscriptionCycleJobService $cycleJobService */
|
||||
private $cycleJobService;
|
||||
|
||||
/** @var TaskMutexServiceInterface $taskMutexService */
|
||||
private $taskMutexService;
|
||||
|
||||
/** @var SubscriptionCycleModuleInterface $subscriptionCycleModule */
|
||||
private $subscriptionCycleModule;
|
||||
|
||||
/** @var SubscriptionModuleInterface $subscriptionModule */
|
||||
private $subscriptionModule;
|
||||
|
||||
/** @var bool $useGroups */
|
||||
private $useGroups;
|
||||
|
||||
/**
|
||||
* SubscriptionCycleManualJobTask constructor.
|
||||
*
|
||||
* @param ApplicationCore $app
|
||||
* @param Database $db
|
||||
* @param SubscriptionCycleJobService $cycleJobService
|
||||
* @param SubscriptionCycleModuleInterface $subscriptionCycleModule
|
||||
* @param bool $useGroups
|
||||
*/
|
||||
/**
|
||||
* SubscriptionCycleManualJobTask constructor.
|
||||
*
|
||||
* @param ApplicationCore $app
|
||||
* @param Database $db
|
||||
* @param TaskMutexServiceInterface $taskMutexService
|
||||
* @param SubscriptionCycleJobService $cycleJobService
|
||||
* @param SubscriptionModuleInterface $subscriptionModule
|
||||
*/
|
||||
public function __construct(
|
||||
ApplicationCore $app,
|
||||
Database $db,
|
||||
TaskMutexServiceInterface $taskMutexService,
|
||||
SubscriptionCycleJobService $cycleJobService,
|
||||
SubscriptionCycleModuleInterface $subscriptionCycleModule,
|
||||
SubscriptionModuleInterface $subscriptionModule,
|
||||
bool $useGroups
|
||||
SubscriptionModuleInterface $subscriptionModule
|
||||
) {
|
||||
$this->app = $app;
|
||||
$this->db = $db;
|
||||
$this->taskMutexService = $taskMutexService;
|
||||
$this->cycleJobService = $cycleJobService;
|
||||
$this->subscriptionCycleModule = $subscriptionCycleModule;
|
||||
$this->subscriptionModule = $subscriptionModule;
|
||||
$this->useGroups = $useGroups;
|
||||
}
|
||||
|
||||
public function execute(): void
|
||||
@ -71,77 +57,16 @@ final class SubscriptionCycleManualJobTask
|
||||
}
|
||||
$this->taskMutexService->setMutex('rechnungslauf_manual');
|
||||
$jobs = $this->cycleJobService->listAll(100);
|
||||
$simulatedDays = $this->getSimulatedDates($jobs);
|
||||
if (empty($jobs)) {
|
||||
return;
|
||||
}
|
||||
foreach (['auftrag', 'rechnung'] as $doctype) {
|
||||
foreach ($simulatedDays as $simulatedDay) {
|
||||
if ($simulatedDay === '') {
|
||||
$simulatedDay = null;
|
||||
} else {
|
||||
try {
|
||||
$simulatedDay = new DateTimeImmutable($simulatedDay);
|
||||
} catch (Exception $exception) {
|
||||
$simulatedDay = null;
|
||||
}
|
||||
}
|
||||
$addresses = $this->getAddressesByTypeFromJobs($jobs, $doctype, $simulatedDay);
|
||||
foreach ($jobs as $job) {
|
||||
$job = $this->cycleJobService->getJob((int)$job['id']);
|
||||
if (empty($job)) {
|
||||
continue;
|
||||
}
|
||||
if ($job['document_type'] !== $doctype) {
|
||||
continue;
|
||||
}
|
||||
if ($job['simulated_day'] === null && $simulatedDay !== null) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
$job['simulated_day'] !== null
|
||||
&& ($simulatedDay === null || $simulatedDay->format('Y-m-d') !== $job['simulated_day'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($job['address_id'], $addresses, false)) {
|
||||
$this->cycleJobService->delete((int)$job['id']);
|
||||
continue;
|
||||
}
|
||||
$simulatedDay = null;
|
||||
if ($job['simulated_day'] !== null) {
|
||||
try {
|
||||
$simulatedDay = new DateTimeImmutable($job['simulated_day']);
|
||||
} catch (Exception $exception) {
|
||||
$simulatedDay = null;
|
||||
}
|
||||
}
|
||||
if ($this->useGroups) {
|
||||
$this->subscriptionCycleModule->generateAndSendSubscriptionCycleGroups(
|
||||
$this->subscriptionModule,
|
||||
[$job['address_id']],
|
||||
$doctype,
|
||||
$job['job_type'],
|
||||
$job['printer_id'],
|
||||
$simulatedDay
|
||||
);
|
||||
} else {
|
||||
$this->subscriptionCycleModule->generateAndSendSubscriptionCycle(
|
||||
$this->subscriptionModule,
|
||||
[$job['address_id']],
|
||||
$doctype,
|
||||
$job['printer_id'],
|
||||
$job['job_type'],
|
||||
$simulatedDay
|
||||
);
|
||||
}
|
||||
$this->cycleJobService->delete((int)$job['id']);
|
||||
if ($this->taskMutexService->isTaskInstanceRunning('rechnungslauf')) {
|
||||
return;
|
||||
}
|
||||
$this->taskMutexService->setMutex('rechnungslauf_manual');
|
||||
}
|
||||
}
|
||||
foreach ($jobs as $job) {
|
||||
switch ($job['document_type']) {
|
||||
case 'rechnung':
|
||||
$this->subscriptionModule->CreateInvoice((int)$job['address_id']);
|
||||
break;
|
||||
case 'auftrag':
|
||||
$this->subscriptionModule->CreateOrder((int)$job['address_id']);
|
||||
break;
|
||||
}
|
||||
$this->cycleJobService->delete((int)$job['id']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,52 +74,4 @@ final class SubscriptionCycleManualJobTask
|
||||
{
|
||||
$this->taskMutexService->setMutex('rechnungslauf_manual', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $jobs
|
||||
* @param string $documentType
|
||||
* @param DateTimeInterface|null $simulatedDay
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAddressesByTypeFromJobs(
|
||||
array $jobs,
|
||||
string $documentType,
|
||||
?DateTimeInterface $simulatedDay = null
|
||||
): array {
|
||||
$addresses = [];
|
||||
foreach ($jobs as $job) {
|
||||
if ($job['document_type'] === $documentType) {
|
||||
$addresses[] = (int)$job['address_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($addresses)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$addressesWithSubscriptions = array_keys(
|
||||
(array)$this->subscriptionModule->GetRechnungsArray($documentType, true)
|
||||
);
|
||||
|
||||
return array_intersect($addresses, $addressesWithSubscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all Dates from Setting "Vergangenes Datum für Abrechnungserstellung" to calc old Subscription cycles
|
||||
*
|
||||
* @param array $jobs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSimulatedDates(array $jobs): array
|
||||
{
|
||||
$simulatedDates = [];
|
||||
foreach ($jobs as $job) {
|
||||
$simulatedDates[] = (string)$job['simulated_day'];
|
||||
}
|
||||
|
||||
return array_unique($simulatedDates);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-FileCopyrightText: 2019 Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
@ -11,7 +16,7 @@ use Xentral\Modules\SubscriptionCycle\Exception\InvalidArgumentException;
|
||||
|
||||
final class SubscriptionCycleJobService
|
||||
{
|
||||
private $db;
|
||||
private Database $db;
|
||||
|
||||
/**
|
||||
* SubscriptionCycleJobService constructor.
|
||||
@ -57,29 +62,27 @@ final class SubscriptionCycleJobService
|
||||
* @param string $documentType
|
||||
* @param string|null $jobType
|
||||
* @param int|null $printerId
|
||||
* @param DateTimeInterface|null $simulatedDay
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function create(int $addressId, string $documentType, ?string $jobType, ?int $printerId, ?DateTimeInterface $simulatedDay = null): int
|
||||
public function create(int $addressId, string $documentType, ?string $jobType = null, ?int $printerId = null): int
|
||||
{
|
||||
$this->ensureDocumentType($documentType);
|
||||
$this->db->perform(
|
||||
'INSERT INTO `subscription_cycle_job`
|
||||
(`address_id`, `document_type`, `job_type`, `printer_id`, `created_at`, `simulated_day`)
|
||||
VALUES (:address_id, :document_type, :job_type, :printer_id, NOW(), :simulated_day)',
|
||||
(`address_id`, `document_type`, `job_type`, `printer_id`, `created_at`)
|
||||
VALUES (:address_id, :document_type, :job_type, :printer_id, NOW())',
|
||||
[
|
||||
'address_id' => $addressId,
|
||||
'document_type' => $documentType,
|
||||
'job_type' => $jobType,
|
||||
'printer_id' => $printerId,
|
||||
'simulated_day' => $simulatedDay === null ? null : $simulatedDay->format('Y-m-d'),
|
||||
]
|
||||
);
|
||||
|
||||
return (int)$this->db->lastInsertId();
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
115
classes/Modules/SubscriptionCycle/SubscriptionModule.php
Normal file
115
classes/Modules/SubscriptionCycle/SubscriptionModule.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
namespace Xentral\Modules\SubscriptionCycle;
|
||||
|
||||
use ApplicationCore;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
class SubscriptionModule implements SubscriptionModuleInterface
|
||||
{
|
||||
private ApplicationCore $app;
|
||||
private Database $db;
|
||||
|
||||
public function __construct(ApplicationCore $app, Database $db)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function GetPositions(int $address, string $documentType, DateTimeInterface $calculationDate = null): array
|
||||
{
|
||||
if ($calculationDate === null)
|
||||
$calculationDate = new DateTimeImmutable('today');
|
||||
|
||||
$sql = "SELECT
|
||||
aa.id,
|
||||
@start := GREATEST(aa.startdatum, aa.abgerechnetbis) as start,
|
||||
@end := IF(aa.enddatum = '0000-00-00' OR aa.enddatum > :calcdate, :calcdate, aa.enddatum) as end,
|
||||
@cycles := CASE
|
||||
WHEN aa.preisart = 'monat' THEN
|
||||
TIMESTAMPDIFF(MONTH, @start, @end)
|
||||
WHEN aa.preisart = 'jahr' THEN
|
||||
TIMESTAMPDIFF(YEAR, @start, @end)
|
||||
WHEN aa.preisart = '30tage' THEN
|
||||
FLOOR(TIMESTAMPDIFF(DAY, @start, @end) / 30)
|
||||
END+1 as cycles,
|
||||
CASE
|
||||
WHEN aa.preisart = 'monat' THEN
|
||||
DATE_ADD(@start, INTERVAL @cycles MONTH)
|
||||
WHEN aa.preisart = 'jahr' THEN
|
||||
DATE_ADD(@start, INTERVAL @cycles YEAR)
|
||||
WHEN aa.preisart = '30tage' THEN
|
||||
DATE_ADD(@start, INTERVAL @cycles*30 DAY )
|
||||
END as newend,
|
||||
aa.preisart,
|
||||
aa.adresse,
|
||||
aa.preis,
|
||||
aa.rabatt,
|
||||
aa.bezeichnung,
|
||||
aa.beschreibung,
|
||||
aa.artikel,
|
||||
aa.menge,
|
||||
aa.waehrung
|
||||
FROM abrechnungsartikel aa
|
||||
JOIN artikel a on aa.artikel = a.id
|
||||
WHERE aa.dokument = :doctype
|
||||
AND greatest(aa.startdatum, aa.abgerechnetbis) <= :calcdate
|
||||
AND (aa.enddatum = '0000-00-00' OR aa.abgerechnetbis < aa.enddatum)
|
||||
AND aa.adresse = :address";
|
||||
|
||||
return $this->db->fetchAll($sql, [
|
||||
'doctype' => $documentType,
|
||||
'calcdate' => $calculationDate->format('Y-m-d'),
|
||||
'address' => $address]);
|
||||
}
|
||||
|
||||
public function CreateInvoice(int $address, DateTimeInterface $calculationDate = null) {
|
||||
$positions = $this->GetPositions($address, 'rechnung', $calculationDate);
|
||||
if(empty($positions))
|
||||
return;
|
||||
|
||||
$invoice = $this->app->erp->CreateRechnung($address);
|
||||
$this->app->erp->LoadRechnungStandardwerte($invoice, $address);
|
||||
foreach ($positions as $pos) {
|
||||
$beschreibung = $pos['beschreibung'];
|
||||
|
||||
$starts = DateTimeImmutable::createFromFormat('Y-m-d', $pos['start'])->format('d.m.Y');
|
||||
$newends = DateTimeImmutable::createFromFormat('Y-m-d', $pos['newend'])->format('d.m.Y');
|
||||
$beschreibung .= "<br>Zeitraum: $starts - $newends";
|
||||
|
||||
$this->app->erp->AddRechnungPositionManuell($invoice, $pos['artikel'], $pos['preis'],
|
||||
$pos['menge']*$pos['cycles'], $pos['bezeichnung'], $beschreibung, $pos['waehrung'], $pos['rabatt']);
|
||||
$this->db->exec("UPDATE abrechnungsartikel SET abgerechnetbis='{$pos['newend']}' WHERE id={$pos['id']}");
|
||||
}
|
||||
$this->app->erp->RechnungNeuberechnen($invoice);
|
||||
//$this->app->erp->BelegFreigabe('rechnung', $invoice);
|
||||
}
|
||||
|
||||
public function CreateOrder(int $address, DateTimeInterface $calculationDate = null) {
|
||||
$positions = $this->GetPositions($address, 'auftrag', $calculationDate);
|
||||
if(empty($positions))
|
||||
return;
|
||||
|
||||
$orderid = $this->app->erp->CreateAuftrag($address);
|
||||
$this->app->erp->LoadAuftragStandardwerte($orderid, $address);
|
||||
foreach ($positions as $pos) {
|
||||
$beschreibung = $pos['beschreibung'];
|
||||
|
||||
$starts = DateTimeImmutable::createFromFormat('Y-m-d', $pos['start'])->format('d.m.Y');
|
||||
$newends = DateTimeImmutable::createFromFormat('Y-m-d', $pos['newend'])->format('d.m.Y');
|
||||
$beschreibung .= "<br>Zeitraum: $starts - $newends";
|
||||
|
||||
$this->app->erp->AddAuftragPositionManuell($orderid, $pos['artikel'], $pos['preis'],
|
||||
$pos['menge']*$pos['cycles'], $pos['bezeichnung'], $beschreibung, $pos['waehrung'], $pos['rabatt']);
|
||||
$this->db->exec("UPDATE abrechnungsartikel SET abgerechnetbis='{$pos['newend']}' WHERE id={$pos['id']}");
|
||||
}
|
||||
$this->app->erp->AuftragNeuberechnen($orderid);
|
||||
//$this->app->erp->BelegFreigabe('auftrag', $orderid);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
namespace Xentral\Modules\SubscriptionCycle;
|
||||
|
||||
@ -8,36 +10,7 @@ use DateTimeInterface;
|
||||
|
||||
interface SubscriptionModuleInterface
|
||||
{
|
||||
/**
|
||||
* @param int $customer
|
||||
* @param string $documentType
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function RechnungKunde($customer, $documentType);
|
||||
|
||||
/**
|
||||
* @param $customer
|
||||
* @param $invoiceGroupKey
|
||||
* @param $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function AuftragImportAbo($customer, $invoiceGroupKey, $key);
|
||||
|
||||
/**
|
||||
* @param $customer
|
||||
* @param $invoiceGroupKey
|
||||
* @param $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function RechnungImportAbo($customer, $invoiceGroupKey, $key);
|
||||
|
||||
/**
|
||||
* @param string $documentType
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function GetRechnungsArray($documentType);
|
||||
public function CreateInvoice(int $address, DateTimeInterface $calculationDate = null);
|
||||
public function CreateOrder(int $address, DateTimeInterface $calculationDate = null);
|
||||
public function GetPositions(int $address, string $documentType, DateTimeInterface $calculationDate = null): array;
|
||||
}
|
||||
|
@ -98692,6 +98692,92 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "subscription_cycle_job",
|
||||
"type": "BASE TABLE",
|
||||
"columns": [
|
||||
{
|
||||
"Field": "id",
|
||||
"Type": "int(11)",
|
||||
"Collation": null,
|
||||
"Null": "NO",
|
||||
"Key": "PRI",
|
||||
"Default": "",
|
||||
"Extra": "auto_increment",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
},
|
||||
{
|
||||
"Field": "address_id",
|
||||
"Type": "int(11)",
|
||||
"Collation": null,
|
||||
"Null": "NO",
|
||||
"Key": "MUL",
|
||||
"Default": "",
|
||||
"Extra": "",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
},
|
||||
{
|
||||
"Field": "document_type",
|
||||
"Type": "varchar(32)",
|
||||
"Collation": "utf8mb3_general_ci",
|
||||
"Null": "NO",
|
||||
"Key": "",
|
||||
"Default": "",
|
||||
"Extra": "",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
},
|
||||
{
|
||||
"Field": "job_type",
|
||||
"Type": "varchar(32)",
|
||||
"Collation": "utf8mb3_general_ci",
|
||||
"Null": "YES",
|
||||
"Key": "",
|
||||
"Default": "",
|
||||
"Extra": "",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
},
|
||||
{
|
||||
"Field": "printer_id",
|
||||
"Type": "int(11)",
|
||||
"Collation": "",
|
||||
"Null": "YES",
|
||||
"Key": "",
|
||||
"Default": "",
|
||||
"Extra": "",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
},
|
||||
{
|
||||
"Field": "created_at",
|
||||
"Type": "timestamp",
|
||||
"Collation": "",
|
||||
"Null": "NO",
|
||||
"Key": "",
|
||||
"Default": "current_timestamp()",
|
||||
"Extra": "",
|
||||
"Privileges": "select,insert,update,references",
|
||||
"Commant": ""
|
||||
}
|
||||
],
|
||||
"keys": [
|
||||
{
|
||||
"Key_name": "PRIMARY",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Key_name": "address",
|
||||
"columns": [
|
||||
"address_id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "supersearch_index_group",
|
||||
"type": "BASE TABLE",
|
||||
|
12
www/pages/content/rechnungslauf_abos.tpl
Normal file
12
www/pages/content/rechnungslauf_abos.tpl
Normal file
@ -0,0 +1,12 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
-->
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>
|
||||
<div class="ui-tabs-panel">
|
||||
[TAB1]
|
||||
</div>
|
||||
</div>
|
40
www/pages/content/rechnungslauf_list.tpl
Normal file
40
www/pages/content/rechnungslauf_list.tpl
Normal file
@ -0,0 +1,40 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
-->
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">Rechnungen</a></li>
|
||||
<li><a href="#tabs-2">Aufträge</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1">
|
||||
[MESSAGE_INVOICES]
|
||||
<form method="post" action="#">
|
||||
[TAB_INVOICES]
|
||||
<fieldset>
|
||||
<legend>{|Stapelverarbeitung|}</legend>
|
||||
<input type="checkbox" id="auswahlalle" onchange="alleauswaehlen('#rechnungslauf_invoices');" /> {|alle markieren|}
|
||||
<input type="submit" class="btnBlue" name="createInvoices" value="{|ausführen|}" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div id="tabs-2">
|
||||
[MESSAGE_ORDERS]
|
||||
<form method="post" action="#">
|
||||
[TAB_ORDERS]
|
||||
<fieldset>
|
||||
<legend>{|Stapelverarbeitung|}</legend>
|
||||
<input type="checkbox" id="auswahlalle" onchange="alleauswaehlen('#rechnungslauf_orders');" /> {|alle markieren|}
|
||||
<input type="submit" class="btnBlue" name="createOrders" value="{|ausführen|}" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function alleauswaehlen(target)
|
||||
{
|
||||
var wert = $('#auswahlalle').prop('checked');
|
||||
$(target).find(':checkbox').prop('checked',wert);
|
||||
}
|
||||
</script>
|
@ -1,3 +1,8 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
SPDX-FileCopyrightText: 2019 Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg
|
||||
SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
-->
|
||||
<fieldset>
|
||||
<legend>[SUBHEADING]</legend>
|
||||
|
||||
@ -8,7 +13,7 @@
|
||||
<th>{|Menge|}</th>
|
||||
<th>{|Einzelpreis (netto)|}</th>
|
||||
<th>{|Rabatt|}</th>
|
||||
<th>{|UST|}</th>
|
||||
<th>{|Zyklen|}</th>
|
||||
<th>{|Gesamtpreis (netto)|}</th>
|
||||
<th>{|Währung|}</th>
|
||||
</tr>
|
||||
|
283
www/pages/rechnungslauf.php
Normal file
283
www/pages/rechnungslauf.php
Normal file
@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Andreas Palm
|
||||
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
|
||||
*/
|
||||
|
||||
use Xentral\Modules\SubscriptionCycle\Service\SubscriptionCycleJobService;
|
||||
use Xentral\Modules\SubscriptionCycle\SubscriptionModule;
|
||||
|
||||
class Rechnungslauf {
|
||||
/** @var Application $app */
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
* @param string $name
|
||||
* @param array $erlaubtevars
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function TableSearch($app, $name, $erlaubtevars)
|
||||
{
|
||||
// in dieses switch alle lokalen Tabellen (diese Live Tabellen mit Suche etc.) für dieses Modul
|
||||
switch($name)
|
||||
{
|
||||
case 'rechnungslauf_invoices':
|
||||
case 'rechnungslauf_orders':
|
||||
if ($name == 'rechnungslauf_invoices')
|
||||
$doctype = 'rechnung';
|
||||
else
|
||||
$doctype = 'auftrag';
|
||||
$allowed['rechnungslauf'] = ['rechnungslauf'];
|
||||
|
||||
$heading = array('', '', 'Kunden Nr.', 'Firma/Name', 'Anschreiben', 'E-Mail', 'Projekt', 'Von', 'Bis', 'Betrag netto', 'Menü');
|
||||
$width = ['1%', '1%', '10%', '20%', '10%', '10%', '10%', '10%', '10%', '10%', '1%'];
|
||||
|
||||
$findcols = [
|
||||
'',
|
||||
'',
|
||||
'adr.kundennummer',
|
||||
'adr.name',
|
||||
'adr.anschreiben',
|
||||
"adr.email",
|
||||
'p.abkuerzung',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
];
|
||||
$searchsql = ['adr.kundennummer', 'adr.name', 'adr.email'];
|
||||
|
||||
$numbercols = [2, 10];
|
||||
$alignright = [3, 10];
|
||||
$datecols = [7, 8];
|
||||
|
||||
$defaultorder = 3;
|
||||
$defaultorderdesc = 0;
|
||||
$sumcol = [10];
|
||||
|
||||
$menu = '<table cellpadding="0" cellspacing="0">';
|
||||
$menu .= '<tr>';
|
||||
$menu .= '<td nowrap>';
|
||||
$menu .= '<a href="index.php?module=adresse&action=artikel&id=%value%">';
|
||||
$menu .= '<img src="themes/' . $app->Conf->WFconf['defaulttheme'] . '/images/forward.svg" border="0">';
|
||||
$menu .= '</a>';
|
||||
$menu .= '</td>';
|
||||
$menu .= '</tr>';
|
||||
$menu .= '</table>';
|
||||
|
||||
$calcdate = new \DateTimeImmutable('today');
|
||||
$scalcdate = $calcdate->format('Y-m-d');
|
||||
$where = " aa.id > 0
|
||||
AND aa.dokument = '$doctype'
|
||||
AND greatest(aa.startdatum, aa.abgerechnetbis) < '$scalcdate'
|
||||
AND (aa.enddatum = '0000-00-00' OR aa.abgerechnetbis < aa.enddatum)";
|
||||
|
||||
$sql = "SELECT SQL_CALC_FOUND_ROWS
|
||||
adr.id,
|
||||
'<img src=\"./themes/new/images/details_open.png\" class=\"details\">' as open,
|
||||
concat('<input type=\"checkbox\" name=\"selection[]\" value=\"',adr.id,'\" />') as auswahl,
|
||||
adr.kundennummer,
|
||||
adr.name,
|
||||
adr.anschreiben,
|
||||
adr.email,
|
||||
p.abkuerzung,
|
||||
GROUP_CONCAT(DATE_FORMAT(@start := GREATEST(aa.startdatum, aa.abgerechnetbis),'%d.%m.%Y') SEPARATOR '<br>') as start,
|
||||
GROUP_CONCAT(DATE_FORMAT(
|
||||
@end := CASE
|
||||
WHEN aa.preisart = 'monat' THEN
|
||||
DATE_ADD(@start, INTERVAL TIMESTAMPDIFF(MONTH, @start, IF(aa.enddatum = '0000-00-00' OR aa.enddatum > '$scalcdate', '$scalcdate', aa.enddatum))+1 MONTH)
|
||||
WHEN aa.preisart = 'jahr' THEN
|
||||
DATE_ADD(@start, INTERVAL TIMESTAMPDIFF(YEAR, @start, IF(aa.enddatum = '0000-00-00' OR aa.enddatum > '$scalcdate', '$scalcdate', aa.enddatum))+1 YEAR)
|
||||
WHEN aa.preisart = '30tage' THEN
|
||||
DATE_ADD(@start, INTERVAL (FLOOR(TIMESTAMPDIFF(DAY, @start, IF(aa.enddatum = '0000-00-00' OR aa.enddatum > '$scalcdate', '$scalcdate', aa.enddatum)) / 30)+1)*30 DAY )
|
||||
END, '%d.%m.%Y') SEPARATOR '<br>') as end,
|
||||
SUM((100-aa.rabatt)/100 * aa.preis * aa.menge *
|
||||
(CASE
|
||||
WHEN aa.preisart = 'monat' THEN
|
||||
TIMESTAMPDIFF(MONTH, @start, @end)
|
||||
WHEN aa.preisart = 'jahr' THEN
|
||||
TIMESTAMPDIFF(YEAR, @start, @end)
|
||||
WHEN aa.preisart = '30tage' THEN
|
||||
FLOOR(TIMESTAMPDIFF(DAY, @start, @end) / 30)
|
||||
END
|
||||
)
|
||||
) as amount,
|
||||
adr.id
|
||||
FROM abrechnungsartikel aa
|
||||
JOIN artikel a ON aa.artikel = a.id
|
||||
JOIN adresse adr ON aa.adresse = adr.id
|
||||
LEFT JOIN projekt p ON aa.projekt = p.id";
|
||||
|
||||
$groupby = " GROUP BY aa.adresse, aa.projekt";
|
||||
|
||||
$count = "SELECT count(aa.id)
|
||||
FROM `abrechnungsartikel` AS `aa`
|
||||
WHERE $where $groupby";
|
||||
$menucol = 10;
|
||||
$moreinfo = true;
|
||||
break;
|
||||
|
||||
case 'rechnungslauf_abos':
|
||||
$allowed['rechnungslauf'] = ['abos'];
|
||||
$heading = array(
|
||||
'Kunde',
|
||||
'Kunden Nr.',
|
||||
'Bezeichnung',
|
||||
'Nummer',
|
||||
'Abgerechnet bis',
|
||||
'Enddatum',
|
||||
'Preis',
|
||||
'Rabatt',
|
||||
'Menge',
|
||||
'Art',
|
||||
'Zahlperiode',
|
||||
'Zahlweise',
|
||||
'Dokument',
|
||||
'Menü');
|
||||
$width = ['10%','5%','15%','1','1','1','1','1','1','1','1','1','1','1'];
|
||||
|
||||
$findcols = [
|
||||
'ad.name',
|
||||
'ad.kundennummer',
|
||||
'aa.bezeichnung',
|
||||
'a.nummer',
|
||||
"DATE_FORMAT(aa.abgerechnetbis, '%d.%m.%Y')",
|
||||
'aa.enddatum',
|
||||
'aa.preis',
|
||||
'aa.rabatt',
|
||||
'aa.menge',
|
||||
'aa.preisart',
|
||||
'aa.zahlzyklus',
|
||||
'',
|
||||
'aa.dokument'
|
||||
];
|
||||
$searchsql = ['ad.name', 'aa.bezeichnung'];
|
||||
|
||||
$numbercols = [0];
|
||||
$alignright = [7];
|
||||
$datecols = [5,6];
|
||||
|
||||
$defaultorder = 1;
|
||||
$defaultorderdesc = 0;
|
||||
|
||||
$menu = "<table cellpadding=0 cellspacing=0><tr><td nowrap>"
|
||||
. "<a href=\"index.php?module=adresse&action=artikel&id=%value%\">"
|
||||
. "<img src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/forward.svg\" border=\"0\"></a>"
|
||||
. " </td></tr></table>";
|
||||
|
||||
$where = " aa.id > 0 AND (aa.enddatum = '0000-00-00' OR aa.abgerechnetbis < aa.enddatum) ";
|
||||
|
||||
$sql = "SELECT SQL_CALC_FOUND_ROWS aa.id, ad.name, ad.kundennummer,
|
||||
aa.bezeichnung, a.nummer, DATE_FORMAT(aa.abgerechnetbis, '%d.%m.%Y'),
|
||||
DATE_FORMAT(aa.enddatum, '%d.%m.%Y'),
|
||||
".$app->erp->FormatPreis('aa.preis', 2).", aa.rabatt, aa.menge,
|
||||
aa.preisart, aa.zahlzyklus, '', aa.dokument, ad.id
|
||||
FROM `abrechnungsartikel` AS `aa`
|
||||
LEFT JOIN `adresse` AS `ad` ON aa.adresse = ad.id
|
||||
LEFT JOIN `artikel` AS `a` ON aa.artikel = a.id";
|
||||
|
||||
$count = "SELECT count(aa.id)
|
||||
FROM `abrechnungsartikel` AS `aa`
|
||||
WHERE $where";
|
||||
break;
|
||||
}
|
||||
|
||||
$erg = [];
|
||||
|
||||
foreach($erlaubtevars as $k => $v) {
|
||||
if(isset($$v)) {
|
||||
$erg[$v] = $$v;
|
||||
}
|
||||
}
|
||||
|
||||
return $erg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rechnungslauf constructor.
|
||||
*
|
||||
* @param Application $app
|
||||
* @param bool $intern
|
||||
*/
|
||||
public function __construct($app, $intern = false) {
|
||||
$this->app = $app;
|
||||
if($intern) {
|
||||
return;
|
||||
}
|
||||
$this->app->ActionHandlerInit($this);
|
||||
|
||||
// ab hier alle Action Handler definieren die das Modul hat
|
||||
$this->app->ActionHandler('rechnungslauf', 'ActionList');
|
||||
$this->app->ActionHandler('abos', 'ActionAbos');
|
||||
$this->app->ActionHandler('minidetail', 'ActionMinidetail');
|
||||
|
||||
$this->app->ActionHandlerListen($app);
|
||||
}
|
||||
|
||||
public function MenuList() {
|
||||
$this->app->erp->Headlines("Abolauf");
|
||||
$this->app->erp->MenuEintrag("index.php?module=rechnungslauf&action=rechnungslauf", "Übersicht");
|
||||
$this->app->erp->MenuEintrag("index.php?module=rechnungslauf&action=abos", "gebuchte Abos");
|
||||
$this->app->erp->MenuEintrag("index.php?module=rechnungslauf&action=einstellungen", "Einstellungen");
|
||||
}
|
||||
|
||||
public function ActionList() {
|
||||
/** @var SubscriptionModule $module */
|
||||
$this->MenuList();
|
||||
$this->app->YUI->TableSearch("TAB_INVOICES", 'rechnungslauf_invoices','show', '', '', basename(__FILE__), __CLASS__);
|
||||
$this->app->YUI->TableSearch("TAB_ORDERS", 'rechnungslauf_orders','show', '', '', basename(__FILE__), __CLASS__);
|
||||
if ($this->app->Secure->GetPOST('createInvoices') !== '') {
|
||||
$selection = $this->app->Secure->GetPOST('selection');
|
||||
/** @var SubscriptionCycleJobService $subscriptionCycleJobService */
|
||||
$subscriptionCycleJobService = $this->app->Container->get('SubscriptionCycleJobService');
|
||||
foreach ($selection as $value) {
|
||||
$subscriptionCycleJobService->deleteJobsByAddressIdAndDoctype($value, 'rechnung');
|
||||
$subscriptionCycleJobService->create($value, 'rechnung');
|
||||
$this->app->Tpl->addMessage('info', 'Die Rechnungen werden nun im Hintergrund erstellt', false, 'MESSAGE_INVOICES');
|
||||
}
|
||||
}
|
||||
else if ($this->app->Secure->GetPOST('createOrders') !== '') {
|
||||
$selection = $this->app->Secure->GetPOST('selection');
|
||||
/** @var SubscriptionCycleJobService $subscriptionCycleJobService */
|
||||
$subscriptionCycleJobService = $this->app->Container->get('SubscriptionCycleJobService');
|
||||
foreach ($selection as $value) {
|
||||
$subscriptionCycleJobService->deleteJobsByAddressIdAndDoctype($value, 'auftrag');
|
||||
$subscriptionCycleJobService->create($value, 'auftrag');
|
||||
$this->app->Tpl->addMessage('info', 'Die Aufträge werden nun im Hintergrund erstellt', false, 'MESSAGE_ORDERS');
|
||||
}
|
||||
}
|
||||
$this->app->Tpl->Parse('PAGE', 'rechnungslauf_list.tpl');
|
||||
}
|
||||
|
||||
public function ActionAbos() {
|
||||
$this->MenuList();
|
||||
$this->app->YUI->TableSearch("TAB1", 'rechnungslauf_abos', 'show', '', '', basename(__FILE__), __CLASS__);
|
||||
$this->app->Tpl->Parse('PAGE', 'rechnungslauf_abos.tpl');
|
||||
}
|
||||
|
||||
public function ActionMinidetail() {
|
||||
/** @var SubscriptionModule $module */
|
||||
$module = $this->app->Container->get('SubscriptionModule');
|
||||
$address = $this->app->Secure->GetGET('id');
|
||||
$pos = $module->GetPositions($address, 'rechnung');
|
||||
foreach ($pos as $p) {
|
||||
$row = '<tr>';
|
||||
$row .= sprintf('<td>%s</td>', $p['bezeichnung']);
|
||||
$row .= sprintf('<td>%s</td>', $p['menge']);
|
||||
$row .= sprintf('<td style="text-align: right">%s</td>',
|
||||
$this->app->erp->number_format_variable($p['preis'], 2));
|
||||
$row .= sprintf('<td>%s</td>', $p['rabatt']);
|
||||
$row .= sprintf('<td>%s</td>', $p['cycles']);
|
||||
$row .= sprintf('<td style="text-align: right">%s</td>',
|
||||
$this->app->erp->number_format_variable($p['preis']*$p['menge']*$p['cycles'], 2));
|
||||
$row .= sprintf('<td>%s</td>', $p['waehrung']);
|
||||
$row .= '</tr>';
|
||||
$this->app->Tpl->Add('INHALT', $row);
|
||||
}
|
||||
$this->app->Tpl->Set('SUBHEADING', 'Kunde');
|
||||
$this->app->Tpl->Output('rechnungslauf_minidetail.tpl');
|
||||
$this->app->ExitXentral();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user