mirror of
https://github.com/Karaka-Management/oms-Billing.git
synced 2026-01-11 15:18:42 +00:00
fix billing process
This commit is contained in:
parent
2adbdc8765
commit
55b0d09005
14
.github/user_bug_report.md
vendored
14
.github/user_bug_report.md
vendored
|
|
@ -8,9 +8,11 @@ assignees: ''
|
|||
---
|
||||
|
||||
# Bug Description
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
# How to Reproduce
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
|
|
@ -19,16 +21,20 @@ Steps to reproduce the behavior:
|
|||
4. See error
|
||||
|
||||
# Expected Behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
# Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
# System Information
|
||||
- System: [e.g. PC or iPhone11, ...]
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- KarakaVersion [e.g. 22]
|
||||
|
||||
- System: [e.g. PC or iPhone11, ...]
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- KarakaVersion [e.g. 22]
|
||||
|
||||
# Additional Information
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
|
|
|||
9
Admin/Install/Admin.install.json
Normal file
9
Admin/Install/Admin.install.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"type": "setting",
|
||||
"name": "1005100003",
|
||||
"content": "[\"en\", \"de\"]",
|
||||
"pattern": "",
|
||||
"module": "Billing"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\Billing\Admin
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
];
|
||||
43
Admin/Install/Admin.php
Normal file
43
Admin/Install/Admin.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\Billing\Admin\Install
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Billing\Admin\Install;
|
||||
|
||||
use phpOMS\Application\ApplicationAbstract;
|
||||
|
||||
/**
|
||||
* Admin class.
|
||||
*
|
||||
* @package Modules\Billing\Admin\Install
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Admin
|
||||
{
|
||||
/**
|
||||
* Install Admin providing
|
||||
*
|
||||
* @param ApplicationAbstract $app Application
|
||||
* @param string $path Module path
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function install(ApplicationAbstract $app, string $path) : void
|
||||
{
|
||||
\Modules\Admin\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Admin.install.json']);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,181 +12,208 @@
|
|||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
use Modules\Billing\Models\NullBill;
|
||||
use phpOMS\Localization\ISO3166NameEnum;
|
||||
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
require_once $this->getData('defaultTemplates')
|
||||
->findFile('.pdf.php')
|
||||
->getAbsolutePath();
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = $this->getData('bill') ?? new NullBill();
|
||||
|
||||
// Set up default pdf template
|
||||
/** @phpstan-import-type DefaultPdf from ../../../../Admin/Install/Media/PdfDefaultTemplate/pdfTemplate.pdf.php */
|
||||
$pdf = new DefaultPdf('P', 'mm', 'A4', true, 'UTF-8', false);
|
||||
|
||||
$creator = $this->getData('bill_creator') ?? 'Jingga';
|
||||
$author = 'Jingga';
|
||||
$title = $this->getData('bill_title') ?? 'Invoice';
|
||||
$subtitle = $this->getData('bill_subtitle') ?? 'Sub title';
|
||||
$keywords = $this->getData('keywords') ?? [];
|
||||
$logoName = $this->getData('bill_logo_name') ?? 'Jingga';
|
||||
$slogan = $this->getData('bill_slogan') ?? 'Business solutions made simple.';
|
||||
$lang = include __DIR__ . '/lang.php';
|
||||
|
||||
$legalCompanyName = $this->getData('legal_company_name') ?? 'Jingga e.K.';
|
||||
$companyAddress = $this->getData('bill_company_address') ?? 'Gartenstr. 26';
|
||||
$companyCity = $this->getData('bill_company_city') ?? '61206 Woellstadt';
|
||||
$companyCEO = $this->getData('bill_company_ceo') ?? 'Dennis Eichhorn';
|
||||
$companyWebsite = $this->getData('bill_company_website') ?? 'www.jingga.app';
|
||||
$companyEmail = $this->getData('bill_company_email') ?? 'info@jingga.app';
|
||||
$companyPhone = $this->getData('bill_company_phone') ?? '+49 0152 ????';
|
||||
$pdf->setHeaderData(
|
||||
__DIR__ . '/logo.png', 15,
|
||||
$this->getData('bill_logo_name') ?? 'Jingga',
|
||||
$this->getData('bill_slogan') ?? 'Business solutions made simple.'
|
||||
);
|
||||
$pdf->setCreator($this->getData('bill_creator') ?? 'Jingga');
|
||||
$pdf->setAuthor($this->getData('bill_creator') ?? 'Jingga');
|
||||
$pdf->setTitle($this->getData('bill_title') ?? $bill->type->getL11n());
|
||||
$pdf->setSubject($this->getData('bill_subtitle') ?? '');
|
||||
$pdf->setKeywords(\implode(', ', $this->getData('keywords') ?? []));
|
||||
$pdf->language = $bill->getLanguage();
|
||||
|
||||
$taxOffice = $this->getData('bill_company_tax_office') ?? 'HRB';
|
||||
$taxId = $this->getData('bill_company_tax_id') ?? 'DE ?????????';
|
||||
$vatId = $this->getData('bill_company_vat_id') ?? 'DE ??????';
|
||||
$pdf->attributes['legal_name'] = $this->getData('legal_company_name') ?? 'Jingga e.K.';
|
||||
$pdf->attributes['address'] = $this->getData('bill_company_address') ?? 'Gartenstr. 26';
|
||||
$pdf->attributes['city'] = $this->getData('bill_company_city') ?? '61206 Woellstadt';
|
||||
|
||||
$bankName = $this->getData('bill_company_bank_name') ?? 'Volksbank Mittelhessen';
|
||||
$bic = $this->getData('bill_company_bic') ?? '';
|
||||
$iban = $this->getData('bill_company_iban') ?? '';
|
||||
$pdf->attributes['ceo'] = $this->getData('bill_company_ceo') ?? 'Dennis Eichhorn';
|
||||
$pdf->attributes['tax_office'] = $this->getData('bill_company_tax_office') ?? 'HRB ???';
|
||||
$pdf->attributes['tax_number'] = $this->getData('bill_company_tax_id') ?? '123456789';
|
||||
|
||||
$billTypeName = $this->getData('bill_type_name') ?? 'INVOICE';
|
||||
$pdf->attributes['bank_name'] = $this->getData('bill_company_bank_name') ?? 'Volksbank Mittelhessen';
|
||||
$pdf->attributes['swift'] = $this->getData('bill_company_swift') ?? '.....';
|
||||
$pdf->attributes['bank_account'] = $this->getData('bill_company_bank_account') ?? '.....';
|
||||
|
||||
$billInvoiceNumber = $this->getData('bill_invoice_no') ?? '';
|
||||
$billInvoiceDate = $this->getData('bill_invoice_date') ?? '';
|
||||
$billServiceDate = $this->getData('bill_service_date') ?? '';
|
||||
$billCustomerNo = $this->getData('bill_customer_no') ?? '';
|
||||
$billPO = $this->getData('bill_po') ?? '';
|
||||
$billDueDate = $this->getData('bill_due_date') ?? '';
|
||||
$pdf->attributes['website'] = $this->getData('bill_company_website') ?? 'www.jingga.app';
|
||||
$pdf->attributes['email'] = $this->getData('bill_company_email') ?? 'info@jingga.app';
|
||||
$pdf->attributes['phone'] = $this->getData('bill_company_phone') ?? '+49 0152 ????';
|
||||
|
||||
$invoiceLines = $this->getData('bill_lines') ?? [];
|
||||
|
||||
$paymentTerms = $this->getData('bill_payment_terms') ?? '';
|
||||
$terms = $this->getData('bill_terms') ?? 'https://jingga.app/terms';
|
||||
$taxes = $this->getData('bill_taxes') ?? ['19%' => '0.00'];
|
||||
$currency = $this->getData('bill_currency') ?? 'EUR';
|
||||
|
||||
// set document information
|
||||
$pdf->SetCreator($creator);
|
||||
$pdf->SetAuthor($author);
|
||||
$pdf->SetTitle($title);
|
||||
$pdf->SetSubject($subtitle);
|
||||
$pdf->SetKeywords(\implode(', ', $keywords));
|
||||
|
||||
// set image scale factor
|
||||
$pdf->SetImageScale(PDF_IMAGE_SCALE_RATIO);
|
||||
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
|
||||
|
||||
// add a page
|
||||
$pdf->AddPage();
|
||||
$topPos = $pdf->getY();
|
||||
|
||||
// Set up default bill template
|
||||
$billTypeName = \strtoupper($bill->type->getL11n());
|
||||
|
||||
// @todo: depending on amount of lines, there is a solution (html, or use backtracking of tcpdf)
|
||||
|
||||
// Address
|
||||
$pdf->SetY(55); // @todo: depending on amount of lines, there is a solution (html, or use backtracking of tcpdf)
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->setY(55);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
|
||||
$countries = ISO3166NameEnum::getConstants();
|
||||
$toCountry = isset($countries[$bill->billCountry]) ? $countries[$bill->billCountry] : '';
|
||||
|
||||
$addressString = \trim(
|
||||
$bill->billTo . "\n"
|
||||
. (!empty($bill->billAddress) ? ($bill->billAddress . "\n") : '')
|
||||
. (!empty($bill->billCity) ? ($bill->billCity . "\n") : '')
|
||||
. (!empty($toCountry) ? ($toCountry . "\n") : ''),
|
||||
"\n "
|
||||
);
|
||||
|
||||
// Count the char "\n" in $addressString
|
||||
$addressLineCount = \substr_count($addressString, "\n") + 1;
|
||||
|
||||
$lineHeight = $pdf->getY();
|
||||
$pdf->Write(0, "Dennis Eichhorn\nGartenstr. 26\n61206 Woellstadt", '', 0, 'L', false, 0, false, false, 0);
|
||||
$lineHeight = ($lineHeight - $pdf->getY()) / 3;
|
||||
$pdf->Write(
|
||||
0,
|
||||
$addressString,
|
||||
'', 0, 'L', false, 0, false, false, 0
|
||||
);
|
||||
$lineHeight = ($lineHeight - $pdf->getY()) / $addressLineCount;
|
||||
|
||||
// Document head
|
||||
$pdf->SetFont('helvetica', 'B', 20);
|
||||
$titleWidth = $pdf->GetStringWidth($billTypeName, 'helvetica', 'B', 20);
|
||||
// Bill head
|
||||
$pdf->setFont('helvetica', 'B', 20);
|
||||
$titleWidth = $pdf->getStringWidth($billTypeName, 'helvetica', 'B', 20);
|
||||
|
||||
$pdf->SetXY(
|
||||
$pdf->setXY(
|
||||
$rightPos = ($pdf->getPageWidth() - $titleWidth - ($titleWidth < 55 ? 55 : 35) + 15),
|
||||
$topPos + 50 + $lineHeight * 3 - 38,
|
||||
$topPos + 50 + $lineHeight * $addressLineCount - 38,
|
||||
true
|
||||
);
|
||||
|
||||
$pdf->SetTextColor(255, 255, 255);
|
||||
$pdf->SetFillColor(255, 162, 7);
|
||||
$pdf->setTextColor(255, 255, 255);
|
||||
$pdf->setFillColor(255, 162, 7);
|
||||
$pdf->Cell($pdf->getPageWidth() - $rightPos - 15, 0, $billTypeName, 0, 0, 'L', true);
|
||||
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->SetTextColor(255, 162, 7);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
$pdf->setTextColor(255, 162, 7);
|
||||
|
||||
$pdf->SetXY($rightPos, $tempY = $pdf->getY() + 10, true);
|
||||
$pdf->MultiCell(23, 30, "Invoice No\nInvoice Date\nService Date\nCustomer No\nPO\nDue Date", 0, 'L');
|
||||
$pdf->setXY($rightPos, $tempY = $pdf->getY() + 10, true);
|
||||
$pdf->MultiCell(
|
||||
23, 30,
|
||||
$lang[$pdf->language]['InvoiceNo'] . "\n"
|
||||
. $lang[$pdf->language]['InvoiceDate'] . "\n"
|
||||
. $lang[$pdf->language]['ServiceDate'] . "\n"
|
||||
. $lang[$pdf->language]['CustomerNo'] . "\n"
|
||||
. $lang[$pdf->language]['PO'] . "\n"
|
||||
. $lang[$pdf->language]['DueDate'],
|
||||
0, 'L'
|
||||
);
|
||||
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
$pdf->setTextColor(0, 0, 0);
|
||||
|
||||
$pdf->SetXY($rightPos + 23 + 2, $tempY, true);
|
||||
$pdf->MultiCell(25, 30, "2022-123456\nYYYY-MM-DD\nYYYY-MM-DD\n123-456-789\n2022-123456\nYYYY-MM-DD", 0, 'L');
|
||||
$pdf->setXY($rightPos + 23 + 2, $tempY, true);
|
||||
$pdf->MultiCell(
|
||||
25, 30,
|
||||
$bill->number . "\n"
|
||||
. $bill->billDate->format('Y-m-d') . "\n"
|
||||
. $bill->performanceDate->format('Y-m-d') . "\n"
|
||||
. $bill->accountNumber . "\n"
|
||||
. '' . "\n" /* @todo: implement customer / supplier reference as string */
|
||||
. $bill->billDate->format('Y-m-d'), /* Consider to add dueDate in addition */
|
||||
0, 'L'
|
||||
);
|
||||
$pdf->Ln();
|
||||
|
||||
$pdf->SetY($pdf->GetY() - 30);
|
||||
$pdf->setY($pdf->getY() - 30);
|
||||
|
||||
/*
|
||||
$pdf->writeHTMLCell(
|
||||
$pdf->getPageWidth() - 15 * 2, 0, null, null,
|
||||
"<strong>Lorem ipsum dolor sit amet,</strong><br \><br \>Consectetur adipiscing elit. Vivamus ac massa sit amet eros posuere accumsan feugiat vel est. Maecenas ultricies enim eu eros rhoncus, volutpat cursus enim imperdiet. Aliquam et odio ipsum. Quisque dapibus scelerisque tempor. Phasellus purus lorem, venenatis eget pretium ac, convallis et ante. Aenean pulvinar justo consectetur mi tincidunt venenatis. Suspendisse ultricies enim id nulla facilisis lacinia. <br /><br />Nam congue nunc nunc, eu pellentesque eros aliquam ac. Nunc placerat elementum turpis, quis facilisis diam volutpat at. Suspendisse enim leo, convallis nec ornare eu, auctor nec purus. Nunc neque metus, feugiat quis justo nec, mollis dignissim risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In at ornare sem. Cras placerat, sapien sed ornare lacinia, mauris nulla volutpat nisl, eget dapibus nisl ipsum non est. Suspendisse ut nisl a ipsum rhoncus sodales.",
|
||||
0, 0, false, true, 'J'
|
||||
);
|
||||
$pdf->Ln();
|
||||
*/
|
||||
|
||||
$pdf->SetY($pdf->GetY() + 5);
|
||||
$pdf->setY($pdf->getY() + 5);
|
||||
|
||||
$header = ['Item', 'Quantity', 'Rate', 'Total'];
|
||||
$data = [
|
||||
['ASDF', 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer.</span>", 2.0, "199.90\n-10 %", "150.399.80\n-15.039"],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains. Here we are testing how it looks like if a very long text is posted in the description without any additional line breaks. It should auto-break!</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
["123-456-789<br><strong>This is a item name</strong><br><span style=\"color: #444;\">This is the item description in more detail for the customer so he knows what this content actually contains.</span>", 2.0, 199.90, 399.80],
|
||||
['ASDF', 2.0, 199.90, 399.80],
|
||||
$header = [
|
||||
$lang[$pdf->language]['Item'],
|
||||
$lang[$pdf->language]['Quantity'],
|
||||
$lang[$pdf->language]['UnitPrice'],
|
||||
$lang[$pdf->language]['Total']
|
||||
];
|
||||
|
||||
$lines = $bill->getElements();
|
||||
|
||||
// Header
|
||||
$w = array($pdf->getPageWidth() - 20 - 20 - 20 - 2*15, 20, 20, 20);
|
||||
$num_headers = \count($header);
|
||||
$headerCount = \count($header);
|
||||
$w = [$pdf->getPageWidth() - 20 - 20 - 20 - 2*15, 20, 20, 20];
|
||||
|
||||
$pdf->setCellPadding(1, 1, 1, 1);
|
||||
|
||||
$taxes = [];
|
||||
$first = true;
|
||||
|
||||
// Data
|
||||
$fill = false;
|
||||
foreach($data as $row) {
|
||||
if ($row === null || $first || $pdf->getY() > $pdf->getPageHeight() - 40) {
|
||||
$pdf->SetFillColor(255, 162, 7);
|
||||
$pdf->SetTextColor(255);
|
||||
$pdf->SetDrawColor(255, 162, 7);
|
||||
foreach($lines as $line) {
|
||||
// @todo: add support for empty lines (row = line)
|
||||
if (/*$row === null || */$first || $pdf->getY() > $pdf->getPageHeight() - 40) {
|
||||
$pdf->setFillColor(255, 162, 7);
|
||||
$pdf->setTextColor(255);
|
||||
$pdf->setDrawColor(255, 162, 7);
|
||||
//$pdf->SetLineWidth(0.3);
|
||||
$pdf->SetFont('helvetica', 'B', 8);
|
||||
$pdf->setFont('helvetica', 'B', 8);
|
||||
|
||||
if (!$first || $row === null) {
|
||||
if (!$first/* || $row === null*/) {
|
||||
$pdf->AddPage();
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
for($i = 0; $i < $num_headers; ++$i) {
|
||||
for($i = 0; $i < $headerCount; ++$i) {
|
||||
$pdf->Cell($w[$i], 7, $header[$i], 1, 0, 'L', true);
|
||||
}
|
||||
|
||||
$pdf->Ln();
|
||||
$pdf->SetFillColor(245, 245, 245);
|
||||
$pdf->SetTextColor(0);
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->setFillColor(245, 245, 245);
|
||||
$pdf->setTextColor(0);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
|
||||
$first = false;
|
||||
}
|
||||
|
||||
$tempY = $pdf->getY();
|
||||
$pdf->writeHTMLCell($w[0], 10, null, null, $row[0], 0, 2, $fill);
|
||||
$pdf->writeHTMLCell($w[0], 10, null, null, $line->itemNumber . ' ' . $line->itemName, 0, 2, $fill);
|
||||
$height = $pdf->getY() - $tempY;
|
||||
|
||||
/*
|
||||
$pdf->writeHTMLCell($w[1], $height, 15 + $w[0], $tempY, $row[1], 0, 0, $fill);
|
||||
$pdf->writeHTMLCell($w[2], $height, 15 + $w[0] + $w[1], $tempY, $row[2], 0, 0, $fill);
|
||||
$pdf->writeHTMLCell($w[3], $height, 15 + $w[0] + $w[1] + $w[2], $tempY, $row[3], 0, 1, $fill);
|
||||
*/
|
||||
|
||||
$pdf->MultiCell($w[1], $height, $row[1], 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
$pdf->MultiCell($w[2], $height, $row[2], 0, 'L', $fill, 0, 15 + $w[0] + $w[1], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
$pdf->MultiCell($w[3], $height, $row[3], 0, 'L', $fill, 1, 15 + $w[0] + $w[1] + $w[2], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
$pdf->MultiCell($w[1], $height, $line->getQuantity(), 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
$pdf->MultiCell($w[2], $height, $line->singleSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 0, 15 + $w[0] + $w[1], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
$pdf->MultiCell($w[3], $height, $line->totalSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 1, 15 + $w[0] + $w[1] + $w[2], $tempY, true, 0, false, true, 0, 'M', true);
|
||||
|
||||
$fill = !$fill;
|
||||
|
||||
// get taxes
|
||||
if (!isset($taxes[$line->taxR->getInt() / 100])) {
|
||||
$taxes[$line->taxR->getInt() / 100] = $line->taxP;
|
||||
} else {
|
||||
$taxes[$line->taxR->getInt() / 100]->add($line->taxP);
|
||||
}
|
||||
}
|
||||
|
||||
$pdf->Cell(\array_sum($w), 0, '', 'T');
|
||||
|
|
@ -196,68 +223,71 @@ if ($pdf->getY() > $pdf->getPageHeight() - 40) {
|
|||
$pdf->AddPage();
|
||||
}
|
||||
|
||||
$pdf->SetFillColor(240, 240, 240);
|
||||
$pdf->SetTextColor(0);
|
||||
$pdf->SetDrawColor(240, 240, 240);
|
||||
$pdf->SetFont('helvetica', 'B', 8);
|
||||
$pdf->setFillColor(240, 240, 240);
|
||||
$pdf->setTextColor(0);
|
||||
$pdf->setDrawColor(240, 240, 240);
|
||||
$pdf->setFont('helvetica', 'B', 8);
|
||||
|
||||
$tempY = $pdf->GetY();
|
||||
$tempY = $pdf->getY();
|
||||
|
||||
$pdf->SetX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, 'Subtotal', 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, '40.000', 0, 0, 'L', false);
|
||||
$pdf->Ln();
|
||||
$pdf->SetX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, 'Taxes (0%)', 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, '40.000', 0, 0, 'L', false);
|
||||
$pdf->Ln();
|
||||
$pdf->SetX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, 'Taxes (16%)', 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, '40.000', 0, 0, 'L', false);
|
||||
$pdf->Ln();
|
||||
$pdf->SetX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, 'Taxes (19%)', 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, '40.000', 0, 0, 'L', false);
|
||||
$pdf->setX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, $lang[$pdf->language]['Subtotal'], 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, $bill->netSales->getCurrency(2, symbol: ''), 0, 0, 'L', false);
|
||||
$pdf->Ln();
|
||||
|
||||
$pdf->SetFillColor(255, 162, 7);
|
||||
$pdf->SetTextColor(255);
|
||||
$pdf->SetDrawColor(255, 162, 7);
|
||||
$pdf->SetFont('helvetica', 'B', 8);
|
||||
foreach ($taxes as $rate => $tax) {
|
||||
$pdf->setX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, $lang[$pdf->language]['Taxes'] . ' (' . $rate . '%)', 0, 0, 'L', false);
|
||||
$pdf->Cell($w[3], 7, $tax->getCurrency(2, symbol: ''), 0, 0, 'L', false);
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
$pdf->SetX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, 'TOTAL', 1, 0, 'L', true);
|
||||
$pdf->Cell($w[3], 7, '40.000', 1, 0, 'L', true);
|
||||
// @todo: add currency
|
||||
|
||||
$pdf->setFillColor(255, 162, 7);
|
||||
$pdf->setTextColor(255);
|
||||
$pdf->setDrawColor(255, 162, 7);
|
||||
$pdf->setFont('helvetica', 'B', 8);
|
||||
|
||||
$pdf->setX($w[0] + $w[1] + 15);
|
||||
$pdf->Cell($w[2], 7, \strtoupper($lang[$pdf->language]['Total']), 1, 0, 'L', true);
|
||||
$pdf->Cell($w[3], 7, $bill->grossSales->getCurrency(2, symbol: ''), 1, 0, 'L', true);
|
||||
$pdf->Ln();
|
||||
|
||||
$tempY2 = $pdf->getY();
|
||||
|
||||
$pdf->SetTextColor(0);
|
||||
$pdf->SetFont('helvetica', 'B', 8);
|
||||
$pdf->SetY($tempY);
|
||||
$pdf->Write(0, 'Payment Terms: ', '', 0, 'L', false, 0, false, false, 0);
|
||||
// @todo: fix payment terms
|
||||
$pdf->setTextColor(0);
|
||||
$pdf->setFont('helvetica', 'B', 8);
|
||||
$pdf->setY($tempY);
|
||||
$pdf->Write(0, $lang[$pdf->language]['PaymentTerms'] . ': CreditCard', '', 0, 'L', false, 0, false, false, 0);
|
||||
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->Write(0, 'Payment within 30 business days', '', 0, 'L', false, 0, false, false, 0);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
$pdf->Write(0, $bill->paymentText, '', 0, 'L', false, 0, false, false, 0);
|
||||
$pdf->Ln();
|
||||
|
||||
$pdf->SetFont('helvetica', 'B', 8);
|
||||
$pdf->Write(0, 'Terms: ', '', 0, 'L', false, 0, false, false, 0);
|
||||
// @todo: fix terms
|
||||
$pdf->setFont('helvetica', 'B', 8);
|
||||
$pdf->Write(0, $lang[$pdf->language]['Terms'] . ': https://jingga.app/terms', '', 0, 'L', false, 0, false, false, 0);
|
||||
|
||||
$pdf->SetFont('helvetica', '', 8);
|
||||
$pdf->Write(0, 'https://jingga.app/terms', '', 0, 'L', false, 0, false, false, 0);
|
||||
$pdf->setFont('helvetica', '', 8);
|
||||
$pdf->Write(0, $bill->termsText, '', 0, 'L', false, 0, false, false, 0);
|
||||
$pdf->Ln();
|
||||
|
||||
$pdf->SetY($tempY2);
|
||||
$pdf->setY($tempY2);
|
||||
$pdf->Ln();
|
||||
|
||||
// $pdf->SetY($pdf->GetY() - 30);
|
||||
/*
|
||||
$pdf->writeHTMLCell(
|
||||
$pdf->getPageWidth() - 15 * 2, 0, null, null,
|
||||
"Consectetur adipiscing elit. Vivamus ac massa sit amet eros posuere accumsan feugiat vel est. Maecenas ultricies enim eu eros rhoncus, volutpat cursus enim imperdiet. Aliquam et odio ipsum. Quisque dapibus scelerisque tempor. Phasellus purus lorem, venenatis eget pretium ac, convallis et ante. Aenean pulvinar justo consectetur mi tincidunt venenatis. Suspendisse ultricies enim id nulla facilisis lacinia. Nam congue nunc nunc, eu pellentesque eros aliquam ac.<br /><br />Nunc placerat elementum turpis, quis facilisis diam volutpat at. Suspendisse enim leo, convallis nec ornare eu, auctor nec purus. Nunc neque metus, feugiat quis justo nec, mollis dignissim risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In at ornare sem. Cras placerat, sapien sed ornare lacinia, mauris nulla volutpat nisl, eget dapibus nisl ipsum non est. Suspendisse ut nisl a ipsum rhoncus sodales.",
|
||||
0, 0, false, true, 'J'
|
||||
);
|
||||
$pdf->Ln();
|
||||
*/
|
||||
|
||||
//Close and output PDF document
|
||||
$pdf->Output('example_048.pdf', 'I');
|
||||
$pdf->Output(
|
||||
$this->getData('path') ?? ($bill->billDate->format('Y-m-d') . '_' . $bill->number . '.pdf'),
|
||||
'I'
|
||||
);
|
||||
|
|
|
|||
42
Admin/Install/Media/lang.php
Normal file
42
Admin/Install/Media/lang.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'en' => [
|
||||
'InvoiceNo' => 'Invoice No.',
|
||||
'InvoiceDate' => 'Invoice Date',
|
||||
'ServiceDate' => 'Service Date',
|
||||
'CustomerNo' => 'Customer No.',
|
||||
'PO' => 'PO',
|
||||
'DueDate' => 'Due Date',
|
||||
'Item' => 'Item',
|
||||
'Quantity' => 'Quantity',
|
||||
'UnitPrice' => 'Unit Price',
|
||||
'Total' => 'Total',
|
||||
'Net' => 'Net',
|
||||
'Gross' => 'Gross',
|
||||
'Subtotal' => 'Subtotal',
|
||||
'Taxes' => 'Taxes',
|
||||
'Terms' => 'Terms',
|
||||
'PaymentTerms' => 'Payment Terms',
|
||||
],
|
||||
'de' => [
|
||||
'InvoiceNo' => 'Belegnummer',
|
||||
'InvoiceDate' => 'Belegdatum',
|
||||
'ServiceDate' => 'Leistungsdatum',
|
||||
'CustomerNo' => 'Kundennummer',
|
||||
'PO' => 'Kundenreferenz',
|
||||
'DueDate' => 'Fälligkeitsdatum',
|
||||
'Item' => 'Artikel',
|
||||
'Quantity' => 'Menge',
|
||||
'UnitPrice' => 'Einzelpreis',
|
||||
'Total' => 'Gesamt',
|
||||
'Net' => 'Netto',
|
||||
'Gross' => 'Brutto',
|
||||
'Subtotal' => 'Netto',
|
||||
'Taxes' => 'USt',
|
||||
'Terms' => 'AGB',
|
||||
'PaymentTerms' => 'Zahlungsbedingungen',
|
||||
],
|
||||
];
|
||||
|
|
@ -393,11 +393,6 @@
|
|||
"type": "VARCHAR(255)",
|
||||
"null": false
|
||||
},
|
||||
"billing_bill_numberformat": {
|
||||
"name": "billing_bill_numberformat",
|
||||
"type": "VARCHAR(255)",
|
||||
"null": false
|
||||
},
|
||||
"billing_bill_info": {
|
||||
"name": "billing_bill_info",
|
||||
"type": "TEXT",
|
||||
|
|
@ -405,7 +400,12 @@
|
|||
},
|
||||
"billing_bill_status": {
|
||||
"name": "billing_bill_status",
|
||||
"type": "INT",
|
||||
"type": "TINYINT",
|
||||
"null": false
|
||||
},
|
||||
"billing_bill_paymentstatus": {
|
||||
"name": "billing_bill_paymentstatus",
|
||||
"type": "TINYINT",
|
||||
"null": false
|
||||
},
|
||||
"billing_bill_type": {
|
||||
|
|
@ -423,6 +423,11 @@
|
|||
"foreignTable": "media",
|
||||
"foreignKey": "media_id"
|
||||
},
|
||||
"billing_bill_account_no": {
|
||||
"name": "billing_bill_account_no",
|
||||
"type": "VARCHAR(50)",
|
||||
"null": false
|
||||
},
|
||||
"billing_bill_supplier": {
|
||||
"name": "billing_bill_supplier",
|
||||
"type": "INT",
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ final class ApiAttributeController extends Controller
|
|||
private function validateBillAttributeCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['type'] = empty($request->getData('type')))
|
||||
|| ($val['value'] = (empty($request->getData('value')) && empty($request->getData('custom'))))
|
||||
|| ($val['bill'] = empty($request->getData('bill')))
|
||||
if (($val['type'] = !$request->hasData('type'))
|
||||
|| ($val['value'] = (!$request->hasData('value') && !$request->hasData('custom')))
|
||||
|| ($val['bill'] = !$request->hasData('bill'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -181,8 +181,8 @@ final class ApiAttributeController extends Controller
|
|||
private function validateBillAttributeTypeL11nCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['title'] = empty($request->getData('title')))
|
||||
|| ($val['type'] = empty($request->getData('type')))
|
||||
if (($val['title'] = !$request->hasData('title'))
|
||||
|| ($val['type'] = !$request->hasData('type'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -252,8 +252,8 @@ final class ApiAttributeController extends Controller
|
|||
private function validateBillAttributeTypeCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['title'] = empty($request->getData('title')))
|
||||
|| ($val['name'] = empty($request->getData('name')))
|
||||
if (($val['title'] = !$request->hasData('title'))
|
||||
|| ($val['name'] = !$request->hasData('name'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -337,8 +337,8 @@ final class ApiAttributeController extends Controller
|
|||
private function validateBillAttributeValueCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['type'] = empty($request->getData('type')))
|
||||
|| ($val['value'] = empty($request->getData('value')))
|
||||
if (($val['type'] = !$request->hasData('type'))
|
||||
|| ($val['value'] = !$request->hasData('value'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -406,8 +406,8 @@ final class ApiAttributeController extends Controller
|
|||
private function validateBillAttributeValueL11nCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['title'] = empty($request->getData('title')))
|
||||
|| ($val['value'] = empty($request->getData('value')))
|
||||
if (($val['title'] = !$request->hasData('title'))
|
||||
|| ($val['value'] = !$request->hasData('value'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,14 @@ use Modules\Billing\Models\BillElementMapper;
|
|||
use Modules\Billing\Models\BillMapper;
|
||||
use Modules\Billing\Models\BillStatus;
|
||||
use Modules\Billing\Models\BillTypeMapper;
|
||||
use Modules\Billing\Models\SettingsEnum;
|
||||
use Modules\Billing\Models\Tax\TaxCombinationMapper;
|
||||
use Modules\ClientManagement\Models\Client;
|
||||
use Modules\ClientManagement\Models\ClientMapper;
|
||||
use Modules\Finance\Models\TaxCode;
|
||||
use Modules\Finance\Models\TaxCodeMapper;
|
||||
use Modules\ItemManagement\Models\Item;
|
||||
use Modules\ItemManagement\Models\ItemL11nMapper;
|
||||
use Modules\ItemManagement\Models\ItemMapper;
|
||||
use Modules\Media\Models\CollectionMapper;
|
||||
use Modules\Media\Models\MediaMapper;
|
||||
|
|
@ -99,7 +105,7 @@ final class ApiBillController extends Controller
|
|||
private function validateBillUpdate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['bill'] = empty($request->getData('bill')))) {
|
||||
if (($val['bill'] = !$request->hasData('bill'))) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +153,6 @@ final class ApiBillController extends Controller
|
|||
return;
|
||||
}
|
||||
|
||||
// @todo: validate vat before creation
|
||||
$bill = $this->createBillFromRequest($request, $response, $data);
|
||||
$this->createBillDatabaseEntry($bill, $request);
|
||||
|
||||
|
|
@ -168,9 +173,9 @@ final class ApiBillController extends Controller
|
|||
{
|
||||
$this->createModel($request->header->account, $bill, BillMapper::class, 'bill', $request->getOrigin());
|
||||
|
||||
$new = clone $bill;
|
||||
$new->buildNumber(); // The bill id is part of the number
|
||||
$this->updateModel($request->header->account, $bill, $new, BillMapper::class, 'bill', $request->getOrigin());
|
||||
$old = clone $bill;
|
||||
$bill->buildNumber(); // The bill id is part of the number
|
||||
$this->updateModel($request->header->account, $old, $bill, BillMapper::class, 'bill', $request->getOrigin());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,7 +186,7 @@ final class ApiBillController extends Controller
|
|||
*
|
||||
* @return Bill The new Bill object with default values
|
||||
*
|
||||
* @todo Validate VAT before creation
|
||||
* @todo Validate VAT before creation (maybe need to add a status when last validated, we don't want to validate every time)
|
||||
* @todo Set the correct date of payment
|
||||
* @todo Use bill and shipping address instead of main address if available
|
||||
* @todo Implement allowed invoice languages and a default invoice language if none match
|
||||
|
|
@ -197,6 +202,7 @@ final class ApiBillController extends Controller
|
|||
$bill->createdBy = new NullAccount($request->header->account);
|
||||
$bill->billDate = new \DateTime('now'); // @todo: Date of payment
|
||||
$bill->performanceDate = new \DateTime('now'); // @todo: Date of payment
|
||||
$bill->accountNumber = $client->number;
|
||||
|
||||
$bill->shipping = 0;
|
||||
$bill->shippingText = '';
|
||||
|
|
@ -204,6 +210,10 @@ final class ApiBillController extends Controller
|
|||
$bill->payment = 0;
|
||||
$bill->paymentText = '';
|
||||
|
||||
$bill->type = BillTypeMapper::get()
|
||||
->where('name', 'sales_invoice')
|
||||
->execute();
|
||||
|
||||
// @todo: use bill and shipping address instead of main address if available
|
||||
$bill->client = $client;
|
||||
$bill->billTo = $client->account->name1;
|
||||
|
|
@ -214,20 +224,60 @@ final class ApiBillController extends Controller
|
|||
|
||||
$bill->setCurrency(ISO4217CharEnum::_EUR);
|
||||
|
||||
// @todo implement allowed invoice languages and a default invoice language if none match
|
||||
// @todo implement client invoice langage (this would allow invoice langauges which are different from the invoice address)
|
||||
$bill->setLanguage(
|
||||
!\in_array(
|
||||
$client->mainAddress->getCountry(),
|
||||
[ISO3166TwoEnum::_DEU, ISO3166TwoEnum::_AUT]
|
||||
)
|
||||
? ISO639x1Enum::_EN
|
||||
: ISO639x1Enum::_DE
|
||||
/** @var \Model\Setting $settings */
|
||||
$settings = $this->app->appSettings->get(null,
|
||||
SettingsEnum::VALID_BILL_LANGUAGES,
|
||||
unit: $this->app->unitId,
|
||||
module: 'Admin'
|
||||
);
|
||||
|
||||
if (empty($settings)) {
|
||||
/** @var \Model\Setting $settings */
|
||||
$settings = $this->app->appSettings->get(null,
|
||||
SettingsEnum::VALID_BILL_LANGUAGES,
|
||||
unit: null,
|
||||
module: 'Admin'
|
||||
);
|
||||
}
|
||||
|
||||
$validLanguages = [];
|
||||
if (!empty($settings)) {
|
||||
$validLanguages = \json_decode($settings->content, true);
|
||||
} else {
|
||||
$validLanguages = [
|
||||
ISO639x1Enum::_EN,
|
||||
];
|
||||
}
|
||||
|
||||
$billLanguage = $validLanguages[0];
|
||||
|
||||
$clientBillLanguage = $client->getAttribute('bill_language')?->value->getValue();
|
||||
if (!empty($clientBillLanguage) && \in_array($clientBillLanguage, $validLanguages)) {
|
||||
$billLanguage = $clientBillLanguage;
|
||||
} else {
|
||||
$clientLanguages = ISO639x1Enum::languageFromCountry($client->mainAddress->getCountry());
|
||||
$clientLanguage = !empty($clientLanguages) ? $clientLanguages[0] : '';
|
||||
|
||||
if (\in_array($clientLanguage, $validLanguages)) {
|
||||
$billLanguage = $clientLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
$bill->setLanguage($billLanguage);
|
||||
|
||||
return $bill;
|
||||
}
|
||||
|
||||
public function createBaseBillElement(Client $client, Item $item, Bill $bill, RequestAbstract $request) : BillElement
|
||||
{
|
||||
$taxCode = $this->app->moduleManager->get('Billing', 'ApiTax')->getTaxCodeFromClientItem($client, $item, $request->getCountry());
|
||||
|
||||
$element = BillElement::fromItem($item, $taxCode, $request->getDataInt('quantity') ?? 1);
|
||||
$element->bill = $request->getDataInt('bill') ?? 0;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a bill from request.
|
||||
*
|
||||
|
|
@ -271,23 +321,22 @@ final class ApiBillController extends Controller
|
|||
$bill = new Bill();
|
||||
$bill->createdBy = new NullAccount($request->header->account);
|
||||
$bill->type = $billType;
|
||||
$bill->numberFormat = $billType->numberFormat;
|
||||
// @todo: use defaultInvoiceAddress or mainAddress. also consider to use billto1, billto2, billto3 (for multiple lines e.g. name2, fao etc.)
|
||||
$bill->billTo = (string) ($request->getData('billto')
|
||||
$bill->billTo = (string) ($request->getDataString('billto')
|
||||
?? ($account->account->name1 . (!empty($account->account->name2)
|
||||
? ', ' . $account->account->name2
|
||||
: ''
|
||||
)));
|
||||
$bill->billAddress = (string) ($request->getData('billaddress') ?? $account->mainAddress->address);
|
||||
$bill->billZip = (string) ($request->getData('billtopostal') ?? $account->mainAddress->postal);
|
||||
$bill->billCity = (string) ($request->getData('billtocity') ?? $account->mainAddress->city);
|
||||
$bill->billAddress = (string) ($request->getDataString('billaddress') ?? $account->mainAddress->address);
|
||||
$bill->billZip = (string) ($request->getDataString('billtopostal') ?? $account->mainAddress->postal);
|
||||
$bill->billCity = (string) ($request->getDataString('billtocity') ?? $account->mainAddress->city);
|
||||
$bill->billCountry = (string) (
|
||||
$request->getData('billtocountry') ?? (
|
||||
$request->getDataString('billtocountry') ?? (
|
||||
($country = $account->mainAddress->getCountry()) === ISO3166TwoEnum::_XXX ? '' : $country)
|
||||
);
|
||||
$bill->client = !$request->hasData('client') ? null : $account;
|
||||
$bill->supplier = !$request->hasData('supplier') ? null : $account;
|
||||
$bill->performanceDate = new \DateTime($request->getData('performancedate') ?? 'now');
|
||||
$bill->performanceDate = new \DateTime($request->getDataString('performancedate') ?? 'now');
|
||||
$bill->setStatus($request->getDataInt('status') ?? BillStatus::ACTIVE);
|
||||
|
||||
return $bill;
|
||||
|
|
@ -305,11 +354,11 @@ final class ApiBillController extends Controller
|
|||
private function validateBillCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['client/supplier'] = (empty($request->getData('client'))
|
||||
&& (empty($request->getData('supplier'))
|
||||
if (($val['client/supplier'] = (!$request->hasData('client')
|
||||
&& (!$request->hasData('supplier')
|
||||
&& ($request->getDataInt('supplier') ?? -1) !== 0)
|
||||
))
|
||||
|| ($val['type'] = (empty($request->getData('type'))))
|
||||
|| ($val['type'] = (!$request->hasData('type')))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -456,8 +505,8 @@ final class ApiBillController extends Controller
|
|||
private function validateMediaAddToBill(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['media'] = (empty($request->getData('media')) && empty($request->getFiles())))
|
||||
|| ($val['bill'] = empty($request->getData('bill')))
|
||||
if (($val['media'] = (!$request->hasData('media') && empty($request->getFiles())))
|
||||
|| ($val['bill'] = !$request->hasData('bill'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -487,12 +536,20 @@ final class ApiBillController extends Controller
|
|||
return;
|
||||
}
|
||||
|
||||
$element = $this->createBillElementFromRequest($request, $response, $data);
|
||||
/** @var \Modules\Billing\Models\Bill $old */
|
||||
$old = BillMapper::get()
|
||||
->with('client')
|
||||
->with('client/attributes')
|
||||
->with('client/attributes/type')
|
||||
->with('client/attributes/value')
|
||||
->where('id', $request->getDataInt('bill') ?? 0)
|
||||
->execute();
|
||||
|
||||
$element = $this->createBillElementFromRequest($request, $response, $old, $data);
|
||||
$this->createModel($request->header->account, $element, BillElementMapper::class, 'bill_element', $request->getOrigin());
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $old */
|
||||
$old = BillMapper::get()->where('id', $element->bill)->execute();
|
||||
$new = $this->updateBillWithBillElement(clone $old, $element, 1);
|
||||
$new = clone $old;
|
||||
$new->addElement($element);
|
||||
$this->updateModel($request->header->account, $old, $new, BillMapper::class, 'bill_element', $request->getOrigin());
|
||||
|
||||
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Bill element', 'Bill element successfully created.', $element);
|
||||
|
|
@ -508,74 +565,32 @@ final class ApiBillController extends Controller
|
|||
* @return BillElement
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @todo in the database the customer localized version should be stored because this is the version which went out
|
||||
*/
|
||||
public function createBillElementFromRequest(RequestAbstract $request, ResponseAbstract $response, $data = null) : BillElement
|
||||
private function createBillElementFromRequest(RequestAbstract $request, ResponseAbstract $response, Bill $bill, $data = null) : BillElement
|
||||
{
|
||||
$element = new BillElement();
|
||||
$element->bill = (int) $request->getData('bill');
|
||||
$element->item = $request->getDataInt('item') ?? 0;
|
||||
|
||||
if ($element->item === null) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
/** @var \Modules\ItemManagement\Models\Item $item */
|
||||
$item = ItemMapper::get()
|
||||
->with('attributes')
|
||||
->with('attributes/type')
|
||||
->with('attributes/value')
|
||||
->with('l11n')
|
||||
->with('l11n/type')
|
||||
->where('id', $element->item)
|
||||
->where('id', $request->getDataInt('item') ?? 0)
|
||||
->where('l11n/type/title', ['name1', 'name2', 'name3'], 'IN')
|
||||
->where('l11n/language', $response->getLanguage())
|
||||
->where('l11n/language', $bill->getLanguage())
|
||||
->execute();
|
||||
|
||||
$element->itemNumber = $item->number;
|
||||
$element->itemName = $item->getL11n('name1')->description;
|
||||
$element->quantity = $request->getDataInt('quantity') ?? 0;
|
||||
|
||||
$element->singleSalesPriceNet = new Money($request->getDataInt('singlesalespricenet') ?? $item->salesPrice->getInt());
|
||||
$element->totalSalesPriceNet = clone $element->singleSalesPriceNet;
|
||||
$element->totalSalesPriceNet->mult($element->quantity);
|
||||
$element = $this->createBaseBillElement($bill->client, $item, $bill, $request);
|
||||
$element->bill = $bill->getId();
|
||||
|
||||
// discounts
|
||||
if ($request->getData('discount_percentage') !== null) {
|
||||
$discount = (int) $request->getData('discount_percentage');
|
||||
|
||||
$element->singleSalesPriceNet
|
||||
->sub((int) ($element->singleSalesPriceNet->getInt() / 100 * $discount));
|
||||
|
||||
$element->totalSalesPriceNet
|
||||
->sub((int) ($element->totalSalesPriceNet->getInt() / 100 * $discount));
|
||||
// @todo: implement a addDiscount function
|
||||
}
|
||||
|
||||
$element->singlePurchasePriceNet = new Money($item->purchasePrice->getInt());
|
||||
$element->totalPurchasePriceNet = clone $element->singlePurchasePriceNet;
|
||||
$element->totalPurchasePriceNet->mult($element->quantity);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update a bill because of a changed bill element (add, remove, change) from request.
|
||||
*
|
||||
* @param Bill $bill Bill
|
||||
* @param BillElement $element Bill element
|
||||
* @param int $type Change type (0 = update, -1 = remove, +1 = add)
|
||||
*
|
||||
* @return Bill
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function updateBillWithBillElement(Bill $bill, BillElement $element, int $type = 1) : Bill
|
||||
{
|
||||
if ($type === 1) {
|
||||
$bill->netSales->add($element->totalSalesPriceNet);
|
||||
$bill->netCosts->add($element->totalPurchasePriceNet);
|
||||
}
|
||||
|
||||
return $bill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to validate bill element creation from request
|
||||
*
|
||||
|
|
@ -588,7 +603,7 @@ final class ApiBillController extends Controller
|
|||
private function validateBillElementCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['bill'] = empty($request->getData('bill')))) {
|
||||
if (($val['bill'] = !$request->hasData('bill'))) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
|
|
@ -597,6 +612,14 @@ final class ApiBillController extends Controller
|
|||
|
||||
public function apiPreviewRender(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
|
||||
{
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = BillMapper::get()
|
||||
->with('type')
|
||||
->with('type/l11n')
|
||||
->with('elements')
|
||||
->where('id', $request->getDataInt('bill') ?? 0)
|
||||
->execute();
|
||||
|
||||
Autoloader::addPath(__DIR__ . '/../../../Resources/');
|
||||
|
||||
$templateId = $request->getData('bill_template', 'int');
|
||||
|
|
@ -634,7 +657,7 @@ final class ApiBillController extends Controller
|
|||
module: 'Admin'
|
||||
);
|
||||
|
||||
if ($settings === false) {
|
||||
if (empty($settings)) {
|
||||
/** @var \Model\Setting[] $settings */
|
||||
$settings = $this->app->appSettings->get(null,
|
||||
[
|
||||
|
|
@ -661,49 +684,45 @@ final class ApiBillController extends Controller
|
|||
$view->setData('defaultTemplates', $defaultTemplates);
|
||||
$view->setData('defaultAssets', $defaultAssets);
|
||||
|
||||
$path = $this->createBillDir($bill);
|
||||
$pdfDir = __DIR__ . '/../../../Modules/Media/Files' . $path;
|
||||
|
||||
$view->setData('bill', $bill);
|
||||
$view->setData('path', $pdfDir . '/' . $request->getData('bill') . '.pdf');
|
||||
$view->setData('path', $pdfDir . '/' .$bill->billDate->format('Y-m-d') . '_' . $bill->number . '.pdf');
|
||||
|
||||
$view->setData('bill_creator', $request->getData('bill_creator'));
|
||||
$view->setData('bill_title', $request->getData('bill_title'));
|
||||
$view->setData('bill_subtitle', $request->getData('bill_subtitle'));
|
||||
$view->setData('keywords', $request->getData('keywords'));
|
||||
$view->setData('bill_logo_name', $request->getData('bill_logo_name'));
|
||||
$view->setData('bill_slogan', $request->getData('bill_slogan'));
|
||||
$view->setData('bill_creator', $request->getDataString('bill_creator'));
|
||||
$view->setData('bill_title', $request->getDataString('bill_title'));
|
||||
$view->setData('bill_subtitle', $request->getDataString('bill_subtitle'));
|
||||
$view->setData('keywords', $request->getDataString('keywords'));
|
||||
$view->setData('bill_logo_name', $request->getDataString('bill_logo_name'));
|
||||
$view->setData('bill_slogan', $request->getDataString('bill_slogan'));
|
||||
|
||||
$view->setData('legal_company_name', $request->getData('legal_company_name'));
|
||||
$view->setData('bill_company_address', $request->getData('bill_company_address'));
|
||||
$view->setData('bill_company_city', $request->getData('bill_company_city'));
|
||||
$view->setData('bill_company_ceo', $request->getData('bill_company_ceo'));
|
||||
$view->setData('bill_company_website', $request->getData('bill_company_website'));
|
||||
$view->setData('bill_company_email', $request->getData('bill_company_email'));
|
||||
$view->setData('bill_company_phone', $request->getData('bill_company_phone'));
|
||||
$view->setData('legal_company_name', $request->getDataString('legal_company_name'));
|
||||
$view->setData('bill_company_address', $request->getDataString('bill_company_address'));
|
||||
$view->setData('bill_company_city', $request->getDataString('bill_company_city'));
|
||||
$view->setData('bill_company_ceo', $request->getDataString('bill_company_ceo'));
|
||||
$view->setData('bill_company_website', $request->getDataString('bill_company_website'));
|
||||
$view->setData('bill_company_email', $request->getDataString('bill_company_email'));
|
||||
$view->setData('bill_company_phone', $request->getDataString('bill_company_phone'));
|
||||
|
||||
$view->setData('bill_company_tax_office', $request->getData('bill_company_tax_office'));
|
||||
$view->setData('bill_company_tax_id', $request->getData('bill_company_tax_id'));
|
||||
$view->setData('bill_company_vat_id', $request->getData('bill_company_vat_id'));
|
||||
$view->setData('bill_company_tax_office', $request->getDataString('bill_company_tax_office'));
|
||||
$view->setData('bill_company_tax_id', $request->getDataString('bill_company_tax_id'));
|
||||
$view->setData('bill_company_vat_id', $request->getDataString('bill_company_vat_id'));
|
||||
|
||||
$view->setData('bill_company_bank_name', $request->getData('bill_company_bank_name'));
|
||||
$view->setData('bill_company_bic', $request->getData('bill_company_bic'));
|
||||
$view->setData('bill_company_iban', $request->getData('bill_company_iban'));
|
||||
$view->setData('bill_company_bank_name', $request->getDataString('bill_company_bank_name'));
|
||||
$view->setData('bill_company_bic', $request->getDataString('bill_company_bic'));
|
||||
$view->setData('bill_company_iban', $request->getDataString('bill_company_iban'));
|
||||
|
||||
$view->setData('bill_type_name', $request->getData('bill_type_name'));
|
||||
$view->setData('bill_type_name', $request->getDataString('bill_type_name'));
|
||||
|
||||
$view->setData('bill_invoice_no', $request->getData('bill_invoice_no'));
|
||||
$view->setData('bill_invoice_date', $request->getData('bill_invoice_date'));
|
||||
$view->setData('bill_service_date', $request->getData('bill_service_date'));
|
||||
$view->setData('bill_customer_no', $request->getData('bill_customer_no'));
|
||||
$view->setData('bill_po', $request->getData('bill_po'));
|
||||
$view->setData('bill_due_date', $request->getData('bill_due_date'));
|
||||
$view->setData('bill_start_text', $request->getDataString('bill_start_text'));
|
||||
$view->setData('bill_lines', $request->getDataString('bill_lines'));
|
||||
$view->setData('bill_end_text', $request->getDataString('bill_end_text'));
|
||||
|
||||
$view->setData('bill_start_text', $request->getData('bill_start_text'));
|
||||
$view->setData('bill_lines', $request->getData('bill_lines'));
|
||||
$view->setData('bill_end_text', $request->getData('bill_end_text'));
|
||||
|
||||
$view->setData('bill_payment_terms', $request->getData('bill_payment_terms'));
|
||||
$view->setData('bill_terms', $request->getData('bill_terms'));
|
||||
$view->setData('bill_taxes', $request->getData('bill_taxes'));
|
||||
$view->setData('bill_currency', $request->getData('bill_currency'));
|
||||
$view->setData('bill_payment_terms', $request->getDataString('bill_payment_terms'));
|
||||
$view->setData('bill_terms', $request->getDataString('bill_terms'));
|
||||
$view->setData('bill_taxes', $request->getDataString('bill_taxes'));
|
||||
$view->setData('bill_currency', $request->getDataString('bill_currency'));
|
||||
|
||||
$pdf = $view->render();
|
||||
|
||||
|
|
@ -729,6 +748,8 @@ final class ApiBillController extends Controller
|
|||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = BillMapper::get()
|
||||
->with('type')
|
||||
->with('type/l11n')
|
||||
->with('elements')
|
||||
->where('id', $request->getDataInt('bill') ?? 0)
|
||||
->execute();
|
||||
|
|
@ -766,7 +787,7 @@ final class ApiBillController extends Controller
|
|||
module: 'Admin'
|
||||
);
|
||||
|
||||
if ($settings === false) {
|
||||
if (empty($settings)) {
|
||||
/** @var \Model\Setting[] $settings */
|
||||
$settings = $this->app->appSettings->get(null,
|
||||
[
|
||||
|
|
@ -792,10 +813,9 @@ final class ApiBillController extends Controller
|
|||
|
||||
$view->setData('defaultTemplates', $defaultTemplates);
|
||||
$view->setData('defaultAssets', $defaultAssets);
|
||||
$view->setData('bill', $bill);
|
||||
|
||||
/**
|
||||
@todo: pass data to bill
|
||||
*/
|
||||
// @todo: add bill data such as company name bank information, ..., etc.
|
||||
|
||||
$pdf = $view->render();
|
||||
|
||||
|
|
@ -812,20 +832,22 @@ final class ApiBillController extends Controller
|
|||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
\file_put_contents($pdfDir . '/' . $request->getData('bill') . '.pdf', $pdf);
|
||||
if (!\is_file($pdfDir . '/' . $request->getData('bill') . '.pdf')) {
|
||||
$billFileName = $bill->billDate->format('Y-m-d') . '_' . $bill->number . '.pdf';
|
||||
|
||||
\file_put_contents($pdfDir . '/' . $billFileName, $pdf);
|
||||
if (!\is_file($pdfDir . '/' . $billFileName)) {
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$media = $this->app->moduleManager->get('Media')->createDbEntry(
|
||||
$media = $this->app->moduleManager->get('Media', 'Api')->createDbEntry(
|
||||
status: [
|
||||
'status' => UploadStatus::OK,
|
||||
'name' => $request->getData('bill') . '.pdf',
|
||||
'name' => $billFileName,
|
||||
'path' => $pdfDir,
|
||||
'filename' => $request->getData('bill') . '.pdf',
|
||||
'size' => \filesize($pdfDir . '/' . $request->getData('bill') . '.pdf'),
|
||||
'filename' => $billFileName,
|
||||
'size' => \filesize($pdfDir . '/' . $billFileName),
|
||||
'extension' => 'pdf',
|
||||
],
|
||||
account: $request->header->account,
|
||||
|
|
@ -846,7 +868,14 @@ final class ApiBillController extends Controller
|
|||
$request->getOrigin()
|
||||
);
|
||||
|
||||
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'PDF', 'Bill Pdf successfully created.', $media);
|
||||
$this->fillJsonResponse(
|
||||
$request,
|
||||
$response,
|
||||
NotificationLevel::OK,
|
||||
'PDF',
|
||||
'Bill Pdf successfully created.',
|
||||
$media
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -897,7 +926,7 @@ final class ApiBillController extends Controller
|
|||
private function validateNoteCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['id'] = empty($request->getData('id')))) {
|
||||
if (($val['id'] = !$request->hasData('id'))) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ final class ApiBillTypeController extends Controller
|
|||
private function validateBillTypeCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['title'] = empty($request->getData('title')))
|
||||
|| ($val['name'] = empty($request->getData('name')))
|
||||
if (($val['title'] = !$request->hasData('title'))
|
||||
|| ($val['name'] = !$request->hasData('name'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -176,8 +176,8 @@ final class ApiBillTypeController extends Controller
|
|||
private function validateBillTypeL11nCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['title'] = empty($request->getData('title')))
|
||||
|| ($val['type'] = empty($request->getData('type')))
|
||||
if (($val['title'] = !$request->hasData('title'))
|
||||
|| ($val['type'] = !$request->hasData('type'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,13 +205,14 @@ final class ApiPriceController extends Controller
|
|||
}
|
||||
|
||||
// Get tax definition
|
||||
$tax = ($request->getData('price_type', 'int') ?? PriceType::SALES)
|
||||
/** @var \Modules\Billing\Models\Tax\TaxCombination $tax */
|
||||
$tax = ($request->getDataInt('price_type') ?? PriceType::SALES) === PriceType::SALES
|
||||
? TaxCombinationMapper::get()
|
||||
->where('itemCode', $request->getData('price_item'))
|
||||
->where('itemCode', $request->getDataInt('price_item'))
|
||||
->where('clientCode', $account->getAttribute('client_code')->getId())
|
||||
->execute()
|
||||
: TaxCombinationMapper::get()
|
||||
->where('itemCode', $request->getData('price_item'))
|
||||
->where('itemCode', $request->getDataInt('price_item'))
|
||||
->where('supplierCode', $account->getAttribute('supplier_code')->getId())
|
||||
->execute();
|
||||
|
||||
|
|
@ -287,9 +288,9 @@ final class ApiPriceController extends Controller
|
|||
$price->discountPercentage = (int) $request->getData('discountPercentage');
|
||||
$price->bonus = (int) $request->getData('bonus');
|
||||
$price->multiply = $request->getDataBool('multiply') ?? false;
|
||||
$price->currency = $request->getData('currency') ?? ISO4217CharEnum::_EUR;
|
||||
$price->start = $request->hasData('start') ? new \DateTime($request->getData('start')) : null;
|
||||
$price->end = $request->hasData('end') ? new \DateTime($request->getData('end')) : null;
|
||||
$price->currency = $request->getDataString('currency') ?? ISO4217CharEnum::_EUR;
|
||||
$price->start = $request->hasData('start') ? new \DateTime($request->getDataString('start')) : null;
|
||||
$price->end = $request->hasData('end') ? new \DateTime($request->getDataString('end')) : null;
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ use Modules\ClientManagement\Models\Client;
|
|||
use Modules\ClientManagement\Models\ClientAttributeTypeMapper;
|
||||
use Modules\ClientManagement\Models\ClientAttributeValue;
|
||||
use Modules\ClientManagement\Models\NullClientAttributeValue;
|
||||
use Modules\Finance\Models\NullTaxCode;
|
||||
use Modules\Finance\Models\TaxCode;
|
||||
use Modules\Finance\Models\TaxCodeMapper;
|
||||
use Modules\ItemManagement\Models\Item;
|
||||
use Modules\ItemManagement\Models\NullItemAttributeValue;
|
||||
use Modules\Organization\Models\UnitMapper;
|
||||
use Modules\SupplierManagement\Models\NullSupplierAttributeValue;
|
||||
use phpOMS\Localization\ISO3166CharEnum;
|
||||
use phpOMS\Message\Http\RequestStatusCode;
|
||||
|
|
@ -42,6 +47,55 @@ use phpOMS\Model\Message\FormValidation;
|
|||
*/
|
||||
final class ApiTaxController extends Controller
|
||||
{
|
||||
public function getTaxCodeFromClientItem(Client $client, Item $item, string $defaultCountry = '') : TaxCode
|
||||
{
|
||||
// @todo: define default sales tax code if none available?!
|
||||
// @todo: consider to actually use a ownsOne reference instead of only a string, this way the next line with the TaxCodeMapper can be removed
|
||||
/** @var \Modules\Billing\Models\Tax\TaxCombination $taxCombination */
|
||||
$taxCombination = TaxCombinationMapper::get()
|
||||
->where('itemCode', $item->getAttribute('sales_tax_code')?->value->getId())
|
||||
->where('clientCode', $client->getAttribute('sales_tax_code')?->value->getId())
|
||||
->execute();
|
||||
|
||||
/** @var \Modules\Finance\Models\TaxCode $taxCode */
|
||||
$taxCode = TaxCodeMapper::get()
|
||||
->where('abbr', $taxCombination->taxCode)
|
||||
->execute();
|
||||
|
||||
// If now tax code could be found, the local tax code should be used.
|
||||
if ($taxCode instanceof NullTaxCode) {
|
||||
/** @var \Modules\Organization\Models\Unit $unit */
|
||||
$unit = UnitMapper::get()
|
||||
->with('mainAddress')
|
||||
->where('id', $this->app->unitId)
|
||||
->execute();
|
||||
|
||||
// Create dummy client
|
||||
$client = new Client();
|
||||
$client->mainAddress = $unit->mainAddress;
|
||||
|
||||
if (!empty($defaultCountry)) {
|
||||
$client->mainAddress->setCountry($defaultCountry);
|
||||
}
|
||||
|
||||
$taxCodeAttribute = $this->getClientTaxCode($client, $unit->mainAddress);
|
||||
|
||||
/** @var \Modules\Billing\Models\Tax\TaxCombination $taxCombination */
|
||||
$t = $item->getAttribute('sales_tax_code');
|
||||
$taxCombination = TaxCombinationMapper::get()
|
||||
->where('itemCode', $item->getAttribute('sales_tax_code')?->value->getId())
|
||||
->where('clientCode', $taxCodeAttribute->getId())
|
||||
->execute();
|
||||
|
||||
/** @var \Modules\Finance\Models\TaxCode $taxCode */
|
||||
$taxCode = TaxCodeMapper::get()
|
||||
->where('abbr', $taxCombination->taxCode)
|
||||
->execute();
|
||||
}
|
||||
|
||||
return $taxCode;
|
||||
}
|
||||
|
||||
public function apiTaxCombinationCreate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
|
||||
{
|
||||
if (!empty($val = $this->validateTaxCombinationCreate($request))) {
|
||||
|
|
@ -94,10 +148,10 @@ final class ApiTaxController extends Controller
|
|||
private function validateTaxCombinationCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['tax_type'] = empty($request->getData('tax_type')))
|
||||
|| ($val['tax_code'] = empty($request->getData('tax_code')))
|
||||
|| ($val['item_code'] = empty($request->getData('item_code')))
|
||||
|| ($val['account_code'] = empty($request->getData('account_code')))
|
||||
if (($val['tax_type'] = !$request->hasData('tax_type'))
|
||||
|| ($val['tax_code'] = !$request->hasData('tax_code'))
|
||||
|| ($val['item_code'] = !$request->hasData('item_code'))
|
||||
|| ($val['account_code'] = !$request->hasData('account_code'))
|
||||
) {
|
||||
return $val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ final class CliController extends Controller
|
|||
/* Type */
|
||||
$type = $this->findSupplierInvoiceType($content, $identifiers['type'], $language);
|
||||
|
||||
/** @var \Modules\Billing\Models\BillType $billTye */
|
||||
/** @var \Modules\Billing\Models\BillType $billType */
|
||||
$billType = BillTypeMapper::get()
|
||||
->where('name', $type)
|
||||
->execute();
|
||||
|
|
@ -274,7 +274,7 @@ final class CliController extends Controller
|
|||
foreach ($lines as $row => $line) {
|
||||
if (\preg_match($match, $line, $found) === 1) {
|
||||
if ($row < $bestPos) {
|
||||
$bestPos = $row;
|
||||
$bestPos = $row;
|
||||
$bestMatch = \trim($found['bill_date']);
|
||||
}
|
||||
|
||||
|
|
@ -292,7 +292,7 @@ final class CliController extends Controller
|
|||
* @param string[] $lines Bill lines
|
||||
* @param array $matches Gross match patterns
|
||||
*
|
||||
* @return string
|
||||
* @return int
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
|
@ -416,7 +416,7 @@ final class CliController extends Controller
|
|||
|
||||
foreach ($formats as $format) {
|
||||
if (($obj = \DateTime::createFromFormat($format, $date)) !== false) {
|
||||
return $obj;
|
||||
return $obj === false ? null : $obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use Modules\Admin\Models\NullAccount;
|
|||
use Modules\Billing\Models\Attribute\BillAttribute;
|
||||
use Modules\ClientManagement\Models\Client;
|
||||
use Modules\Editor\Models\EditorDoc;
|
||||
use Modules\ItemManagement\Models\Item;
|
||||
use Modules\Media\Models\Collection;
|
||||
use Modules\Media\Models\Media;
|
||||
use Modules\Media\Models\NullMedia;
|
||||
|
|
@ -56,14 +55,6 @@ class Bill implements \JsonSerializable
|
|||
*/
|
||||
public string $number = '';
|
||||
|
||||
/**
|
||||
* Number format ID.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $numberFormat = '';
|
||||
|
||||
/**
|
||||
* Bill type.
|
||||
*
|
||||
|
|
@ -82,6 +73,8 @@ class Bill implements \JsonSerializable
|
|||
*/
|
||||
private int $status = BillStatus::DRAFT;
|
||||
|
||||
private int $paymentStatus = BillPaymentStatus::UNPAID;
|
||||
|
||||
/**
|
||||
* Bill created at.
|
||||
*
|
||||
|
|
@ -136,6 +129,8 @@ class Bill implements \JsonSerializable
|
|||
|
||||
public string $language = ISO639x1Enum::_EN;
|
||||
|
||||
public string $accountNumber = '';
|
||||
|
||||
/**
|
||||
* Receiver.
|
||||
*
|
||||
|
|
@ -468,10 +463,10 @@ class Bill implements \JsonSerializable
|
|||
$this->netDiscount = new Money(0);
|
||||
$this->grossDiscount = new Money(0);
|
||||
|
||||
$this->createdAt = new \DateTimeImmutable();
|
||||
$this->createdBy = new NullAccount();
|
||||
$this->referral = new NullAccount();
|
||||
$this->type = new NullBillType();
|
||||
$this->createdAt = new \DateTimeImmutable();
|
||||
$this->createdBy = new NullAccount();
|
||||
$this->referral = new NullAccount();
|
||||
$this->type = new NullBillType();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -510,7 +505,7 @@ class Bill implements \JsonSerializable
|
|||
$this->id,
|
||||
$this->type->getId(),
|
||||
],
|
||||
$this->numberFormat
|
||||
$this->type->numberFormat
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +564,7 @@ class Bill implements \JsonSerializable
|
|||
{
|
||||
foreach ($this->attributes as $attribute) {
|
||||
if ($attribute->type->name === $attrName) {
|
||||
return $attribute->value;
|
||||
return $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -602,6 +597,32 @@ class Bill implements \JsonSerializable
|
|||
$this->status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paymentStatus
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getPaymentStatus() : int
|
||||
{
|
||||
return $this->paymentStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paymentStatus
|
||||
*
|
||||
* @param int $paymentStatus Status
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setPaymentStatus(int $paymentStatus) : void
|
||||
{
|
||||
$this->paymentStatus = $paymentStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set currency.
|
||||
*
|
||||
|
|
@ -709,7 +730,7 @@ class Bill implements \JsonSerializable
|
|||
/**
|
||||
* Get Bill elements.
|
||||
*
|
||||
* @return array
|
||||
* @return BillElement[]
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
|
@ -740,7 +761,7 @@ class Bill implements \JsonSerializable
|
|||
$this->netDiscount->add($element->totalDiscountP->getInt());
|
||||
|
||||
// @todo: Discount might be in quantities
|
||||
$this->grossDiscount->add((int) ($element->taxR * $element->totalDiscountP->getInt() / 1000));
|
||||
$this->grossDiscount->add((int) ($element->taxR->getInt() * $element->totalDiscountP->getInt() / 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -848,7 +869,6 @@ class Bill implements \JsonSerializable
|
|||
return [
|
||||
'id' => $this->id,
|
||||
'number' => $this->number,
|
||||
'numberFormat' => $this->numberFormat,
|
||||
'type' => $this->type,
|
||||
'shipTo' => $this->shipTo,
|
||||
'shipFAO' => $this->shipFAO,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
public int $order = 0;
|
||||
|
||||
/** @todo: consider to reference the model instead of the int, this would make it much easier in other places like the shop */
|
||||
public ?int $item = null;
|
||||
|
||||
public string $itemNumber = '';
|
||||
|
|
@ -47,7 +48,7 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
public string $itemDescription = '';
|
||||
|
||||
public int $quantity = 0;
|
||||
protected int $quantity = 0;
|
||||
|
||||
public Money $singleSalesPriceNet;
|
||||
|
||||
|
|
@ -57,9 +58,9 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
public Money $totalSalesPriceGross;
|
||||
|
||||
public ?FloatInt $singleDiscountP = null;
|
||||
public Money $singleDiscountP;
|
||||
|
||||
public ?FloatInt $totalDiscountP = null;
|
||||
public Money $totalDiscountP;
|
||||
|
||||
public ?FloatInt $singleDiscountR = null;
|
||||
|
||||
|
|
@ -91,19 +92,19 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
/**
|
||||
* Tax amount
|
||||
*
|
||||
* @var null|FloatInt
|
||||
*
|
||||
* @var Money
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public ?FloatInt $taxP = null;
|
||||
public Money $taxP;
|
||||
|
||||
/**
|
||||
* Tax percentage
|
||||
*
|
||||
*
|
||||
* @var null|FloatInt
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public ?FloatInt $taxR = null;
|
||||
public FloatInt $taxR;
|
||||
|
||||
public string $taxCode = '';
|
||||
|
||||
|
|
@ -155,6 +156,12 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
$this->totalProfitNet = new Money();
|
||||
$this->totalProfitGross = new Money();
|
||||
|
||||
$this->singleDiscountP = new Money();
|
||||
$this->totalDiscountP = new Money();
|
||||
|
||||
$this->taxP = new Money();
|
||||
$this->taxR = new FloatInt();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -169,6 +176,21 @@ class BillElement implements \JsonSerializable
|
|||
return $this->id;
|
||||
}
|
||||
|
||||
public function setQuantity(int $quantity) : void
|
||||
{
|
||||
if ($this->quantity === $quantity) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->quantity = $quantity;
|
||||
// @todo: recalculate all the prices!!!
|
||||
}
|
||||
|
||||
public function getQuantity() : int
|
||||
{
|
||||
return $this->quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item.
|
||||
*
|
||||
|
|
@ -183,14 +205,14 @@ class BillElement implements \JsonSerializable
|
|||
$this->item = $item;
|
||||
}
|
||||
|
||||
public static function fromItem(Item $item, TaxCode $code) : self
|
||||
public static function fromItem(Item $item, TaxCode $code, int $quantity = 1) : self
|
||||
{
|
||||
$element = new self();
|
||||
$element->item = $item->getId();
|
||||
$element->itemNumber = $item->number;
|
||||
$element->itemName = $item->getL11n('name1')->description;
|
||||
$element->itemDescription = $item->getL11n('description_short')->description;
|
||||
$element->quantity = 0;
|
||||
$element->quantity = $quantity;
|
||||
|
||||
// @todo: Use pricing instead of the default sales price
|
||||
// @todo: discounts might be in quantities
|
||||
|
|
@ -201,19 +223,19 @@ class BillElement implements \JsonSerializable
|
|||
$element->singlePurchasePriceNet->setInt($item->purchasePrice->getInt());
|
||||
$element->totalPurchasePriceNet->setInt($element->quantity * $item->purchasePrice->getInt());
|
||||
|
||||
$element->singleProfitNet->setInt($element->singleSalesPriceNet->getInt() - $element->singlePurchasePriceNet->getInt());
|
||||
$element->singleProfitNet->setInt($element->singleSalesPriceNet->getInt() - $element->singlePurchasePriceNet->getInt());
|
||||
$element->totalProfitNet->setInt($element->quantity * ($element->totalSalesPriceNet->getInt() - $element->totalPurchasePriceNet->getInt()));
|
||||
|
||||
$element->taxP = new FloatInt((int) (($code->percentageInvoice * $element->totalSalesPriceNet->getInt()) / 1000));
|
||||
|
||||
$element->taxP = new FloatInt((int) (($code->percentageInvoice * $element->totalSalesPriceNet->getInt()) / 10000));
|
||||
$element->taxR = new FloatInt($code->percentageInvoice);
|
||||
$element->taxCode = $code->abbr;
|
||||
|
||||
$element->singleListPriceGross->setInt((int) ($element->singleListPriceNet->getInt() + $element->singleListPriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
$element->totalListPriceGross->setInt((int) ($element->totalListPriceNet->getInt() + $element->totalListPriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
$element->singleSalesPriceGross->setInt((int) ($element->singleSalesPriceNet->getInt() + $element->singleSalesPriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
$element->totalSalesPriceGross->setInt((int) ($element->totalSalesPriceNet->getInt() + $element->totalSalesPriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
$element->singlePurchasePriceGross->setInt((int) ($element->singlePurchasePriceNet->getInt() + $element->singlePurchasePriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
$element->totalPurchasePriceGross->setInt((int) ($element->totalPurchasePriceNet->getInt() + $element->totalPurchasePriceNet->getInt() * $element->taxR->getInt() / 1000));
|
||||
|
||||
$element->singleListPriceGross->setInt((int) ($element->singleListPriceNet->getInt() + $element->singleListPriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
$element->totalListPriceGross->setInt((int) ($element->totalListPriceNet->getInt() + $element->totalListPriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
$element->singleSalesPriceGross->setInt((int) ($element->singleSalesPriceNet->getInt() + $element->singleSalesPriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
$element->totalSalesPriceGross->setInt((int) ($element->totalSalesPriceNet->getInt() + $element->totalSalesPriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
$element->singlePurchasePriceGross->setInt((int) ($element->singlePurchasePriceNet->getInt() + $element->singlePurchasePriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
$element->totalPurchasePriceGross->setInt((int) ($element->totalPurchasePriceNet->getInt() + $element->totalPurchasePriceNet->getInt() * $element->taxR->getInt() / 10000));
|
||||
|
||||
$element->singleProfitGross->setInt($element->singleSalesPriceGross->getInt() - $element->singlePurchasePriceGross->getInt());
|
||||
$element->totalProfitGross->setInt($element->quantity * ($element->totalSalesPriceGross->getInt() - $element->totalPurchasePriceGross->getInt()));
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ class BillMapper extends DataMapperFactory
|
|||
public const COLUMNS = [
|
||||
'billing_bill_id' => ['name' => 'billing_bill_id', 'type' => 'int', 'internal' => 'id'],
|
||||
'billing_bill_number' => ['name' => 'billing_bill_number', 'type' => 'string', 'internal' => 'number'],
|
||||
'billing_bill_numberformat' => ['name' => 'billing_bill_numberformat', 'type' => 'string', 'internal' => 'numberFormat'],
|
||||
'billing_bill_type' => ['name' => 'billing_bill_type', 'type' => 'int', 'internal' => 'type'],
|
||||
'billing_bill_template' => ['name' => 'billing_bill_template', 'type' => 'int', 'internal' => 'template'],
|
||||
'billing_bill_header' => ['name' => 'billing_bill_header', 'type' => 'string', 'internal' => 'header'],
|
||||
'billing_bill_footer' => ['name' => 'billing_bill_footer', 'type' => 'string', 'internal' => 'footer'],
|
||||
'billing_bill_info' => ['name' => 'billing_bill_info', 'type' => 'string', 'internal' => 'info'],
|
||||
'billing_bill_status' => ['name' => 'billing_bill_status', 'type' => 'int', 'internal' => 'status'],
|
||||
'billing_bill_paymentstatus' => ['name' => 'billing_bill_paymentstatus', 'type' => 'int', 'internal' => 'paymentStatus'],
|
||||
'billing_bill_shipTo' => ['name' => 'billing_bill_shipTo', 'type' => 'string', 'internal' => 'shipTo'],
|
||||
'billing_bill_shipFAO' => ['name' => 'billing_bill_shipFAO', 'type' => 'string', 'internal' => 'shipFAO'],
|
||||
'billing_bill_shipAddr' => ['name' => 'billing_bill_shipAddr', 'type' => 'string', 'internal' => 'shipAddress'],
|
||||
|
|
@ -80,6 +80,7 @@ class BillMapper extends DataMapperFactory
|
|||
'billing_bill_paymentterms_text' => ['name' => 'billing_bill_paymentterms_text', 'type' => 'string', 'internal' => 'termsText'],
|
||||
'billing_bill_ship_type' => ['name' => 'billing_bill_ship_type', 'type' => 'int', 'internal' => 'shipping'],
|
||||
'billing_bill_ship_text' => ['name' => 'billing_bill_ship_text', 'type' => 'string', 'internal' => 'shippingText'],
|
||||
'billing_bill_account_no' => ['name' => 'billing_bill_account_no', 'type' => 'string', 'internal' => 'accountNumber'],
|
||||
'billing_bill_client' => ['name' => 'billing_bill_client', 'type' => 'int', 'internal' => 'client'],
|
||||
'billing_bill_supplier' => ['name' => 'billing_bill_supplier', 'type' => 'int', 'internal' => 'supplier'],
|
||||
'billing_bill_created_by' => ['name' => 'billing_bill_created_by', 'type' => 'int', 'internal' => 'createdBy', 'readonly' => true],
|
||||
|
|
|
|||
34
Models/BillPaymentStatus.php
Normal file
34
Models/BillPaymentStatus.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Billing\Models;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* Status for external references
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class BillPaymentStatus extends Enum
|
||||
{
|
||||
public const UNKNOWN = 0;
|
||||
|
||||
public const PAID = 1;
|
||||
|
||||
public const UNPAID = 2;
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ declare(strict_types=1);
|
|||
namespace Modules\Billing\Models;
|
||||
|
||||
use Modules\Media\Models\Collection;
|
||||
use Modules\Media\Models\NullCollection;
|
||||
use phpOMS\Localization\BaseStringL11n;
|
||||
use phpOMS\Localization\ISO639x1Enum;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ final class BillTypeMapper extends DataMapperFactory
|
|||
'external' => null,
|
||||
],
|
||||
'templates' => [
|
||||
'mapper' => MediaMapper::class,
|
||||
'mapper' => CollectionMapper::class,
|
||||
'table' => 'billing_bill_type_media_rel',
|
||||
'external' => 'billing_bill_type_media_rel_dst',
|
||||
'self' => 'billing_bill_type_media_rel_src',
|
||||
|
|
|
|||
|
|
@ -97,17 +97,17 @@ class Price implements \JsonSerializable
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
$this->item = new NullItem();
|
||||
$this->itemgroup = new NullItemAttributeValue();
|
||||
$this->item = new NullItem();
|
||||
$this->itemgroup = new NullItemAttributeValue();
|
||||
$this->itemsegment = new NullItemAttributeValue();
|
||||
$this->itemsection = new NullItemAttributeValue();
|
||||
$this->itemtype = new NullItemAttributeValue();
|
||||
$this->itemtype = new NullItemAttributeValue();
|
||||
|
||||
$this->client = new NullClient();
|
||||
$this->clientgroup = new NullClientAttributeValue();
|
||||
$this->client = new NullClient();
|
||||
$this->clientgroup = new NullClientAttributeValue();
|
||||
$this->clientsegment = new NullClientAttributeValue();
|
||||
$this->clientsection = new NullClientAttributeValue();
|
||||
$this->clienttype = new NullClientAttributeValue();
|
||||
$this->clienttype = new NullClientAttributeValue();
|
||||
|
||||
$this->supplier = new NullSupplier();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
*/
|
||||
abstract class SettingsEnum extends Enum
|
||||
{
|
||||
public const PREVIEW_MEDIA_TYPE = '1005100001_1'; // internally generated preview
|
||||
public const PREVIEW_MEDIA_TYPE = '1005100001'; // internally generated preview
|
||||
|
||||
public const ORIGINAL_MEDIA_TYPE = '1005100001_2'; // original document (mostly supplier invoice/delivery note)
|
||||
public const ORIGINAL_MEDIA_TYPE = '1005100002'; // original document (mostly supplier invoice/delivery note)
|
||||
|
||||
public const VALID_BILL_LANGUAGES = '1005100003'; // List of valid languages for bills
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ class TaxCombination implements \JsonSerializable
|
|||
|
||||
public string $taxCode = '';
|
||||
|
||||
// @todo: consider to add the tax code object directly, it is annoying to make a manuall mapper call which is often required afterwards.
|
||||
|
||||
public int $taxType = BillTaxType::SALES;
|
||||
|
||||
public string $account = '';
|
||||
|
|
|
|||
|
|
@ -787,7 +787,7 @@ echo $this->getData('nav')->render();
|
|||
?>
|
||||
<tr>
|
||||
<td><?= $values['month'] . '/' . \substr((string) $values['year'], -2); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 1000))->getCurrency(); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 10000))->getCurrency(); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -910,7 +910,7 @@ echo $this->getData('nav')->render();
|
|||
?>
|
||||
<tr>
|
||||
<td><?= (string) $values['year']; ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 1000))->getCurrency(); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 10000))->getCurrency(); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -1617,7 +1617,7 @@ echo $this->getData('nav')->render();
|
|||
?>
|
||||
<tr>
|
||||
<td><?= $values['month'] . '/' . \substr((string) $values['year'], -2); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 1000))->getCurrency(); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 10000))->getCurrency(); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -1739,7 +1739,7 @@ echo $this->getData('nav')->render();
|
|||
?>
|
||||
<tr>
|
||||
<td><?= (string) $values['year']; ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 1000))->getCurrency(); ?>
|
||||
<td><?= (new Money(((int) $values['net_sales']) / 10000))->getCurrency(); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<td><?= ((int) $values['customers']); ?>
|
||||
<?php endforeach; ?>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
"SupplierManagement": "1.0.0"
|
||||
},
|
||||
"providing": {
|
||||
"Admin": "*",
|
||||
"Navigation": "*",
|
||||
"Media": "*",
|
||||
"Workflow": "*"
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ final class ApiControllerTest extends \PHPUnit\Framework\TestCase
|
|||
|
||||
$permission = new AccountPermission();
|
||||
$permission->setUnit(1);
|
||||
$permission->setApp('backend');
|
||||
$permission->setApp(2);
|
||||
$permission->setPermission(
|
||||
PermissionType::READ
|
||||
| PermissionType::CREATE
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user