diff --git a/.github/user_bug_report.md b/.github/user_bug_report.md
index 9e5f2a5..4b92a8e 100755
--- a/.github/user_bug_report.md
+++ b/.github/user_bug_report.md
@@ -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.
diff --git a/Admin/Install/Admin.install.json b/Admin/Install/Admin.install.json
new file mode 100644
index 0000000..1b8d939
--- /dev/null
+++ b/Admin/Install/Admin.install.json
@@ -0,0 +1,9 @@
+[
+ {
+ "type": "setting",
+ "name": "1005100003",
+ "content": "[\"en\", \"de\"]",
+ "pattern": "",
+ "module": "Billing"
+ }
+]
\ No newline at end of file
diff --git a/Admin/Install/Admin.install.php b/Admin/Install/Admin.install.php
deleted file mode 100755
index 0965cc2..0000000
--- a/Admin/Install/Admin.install.php
+++ /dev/null
@@ -1,16 +0,0 @@
- __DIR__ . '/Admin.install.json']);
+ }
+}
diff --git a/Admin/Install/Media/bill.pdf.php b/Admin/Install/Media/bill.pdf.php
index a3975e1..e2895b1 100755
--- a/Admin/Install/Media/bill.pdf.php
+++ b/Admin/Install/Media/bill.pdf.php
@@ -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,
"Lorem ipsum dolor sit amet,
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. 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
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer.", 2.0, "199.90\n-10 %", "150.399.80\n-15.039"],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
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!", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 2.0, 199.90, 399.80],
- ["123-456-789
This is a item name
This is the item description in more detail for the customer so he knows what this content actually contains.", 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.
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');
\ No newline at end of file
+$pdf->Output(
+ $this->getData('path') ?? ($bill->billDate->format('Y-m-d') . '_' . $bill->number . '.pdf'),
+ 'I'
+);
diff --git a/Admin/Install/Media/lang.php b/Admin/Install/Media/lang.php
new file mode 100644
index 0000000..252fa3e
--- /dev/null
+++ b/Admin/Install/Media/lang.php
@@ -0,0 +1,42 @@
+ [
+ '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',
+ ],
+];
diff --git a/Admin/Install/db.json b/Admin/Install/db.json
index 84231e5..ac20ad0 100755
--- a/Admin/Install/db.json
+++ b/Admin/Install/db.json
@@ -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",
diff --git a/Controller/ApiAttributeController.php b/Controller/ApiAttributeController.php
index 6d4d30d..f2bc070 100755
--- a/Controller/ApiAttributeController.php
+++ b/Controller/ApiAttributeController.php
@@ -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;
}
diff --git a/Controller/ApiBillController.php b/Controller/ApiBillController.php
index 4ae2e9a..6872e64 100755
--- a/Controller/ApiBillController.php
+++ b/Controller/ApiBillController.php
@@ -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;
}
diff --git a/Controller/ApiBillTypeController.php b/Controller/ApiBillTypeController.php
index ef057f4..fb8d9bb 100755
--- a/Controller/ApiBillTypeController.php
+++ b/Controller/ApiBillTypeController.php
@@ -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;
}
diff --git a/Controller/ApiPriceController.php b/Controller/ApiPriceController.php
index 8f6c32d..c881cdc 100755
--- a/Controller/ApiPriceController.php
+++ b/Controller/ApiPriceController.php
@@ -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;
}
diff --git a/Controller/ApiTaxController.php b/Controller/ApiTaxController.php
index 6ee1acb..de00f16 100755
--- a/Controller/ApiTaxController.php
+++ b/Controller/ApiTaxController.php
@@ -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;
}
diff --git a/Controller/CliController.php b/Controller/CliController.php
index 3a0123f..87c2b28 100755
--- a/Controller/CliController.php
+++ b/Controller/CliController.php
@@ -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;
}
}
diff --git a/Models/Bill.php b/Models/Bill.php
index c648fd2..3fdb03d 100755
--- a/Models/Bill.php
+++ b/Models/Bill.php
@@ -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,
diff --git a/Models/BillElement.php b/Models/BillElement.php
index 7f4cbb2..8e215fc 100755
--- a/Models/BillElement.php
+++ b/Models/BillElement.php
@@ -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()));
diff --git a/Models/BillMapper.php b/Models/BillMapper.php
index a946c90..55b5ef2 100755
--- a/Models/BillMapper.php
+++ b/Models/BillMapper.php
@@ -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],
diff --git a/Models/BillPaymentStatus.php b/Models/BillPaymentStatus.php
new file mode 100644
index 0000000..4f60f86
--- /dev/null
+++ b/Models/BillPaymentStatus.php
@@ -0,0 +1,34 @@
+ 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',
diff --git a/Models/Price/Price.php b/Models/Price/Price.php
index 92bebb4..11d56b0 100755
--- a/Models/Price/Price.php
+++ b/Models/Price/Price.php
@@ -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();
}
diff --git a/Models/SettingsEnum.php b/Models/SettingsEnum.php
index 8fa0992..07af36a 100755
--- a/Models/SettingsEnum.php
+++ b/Models/SettingsEnum.php
@@ -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
}
diff --git a/Models/Tax/TaxCombination.php b/Models/Tax/TaxCombination.php
index cb7b45e..7473578 100755
--- a/Models/Tax/TaxCombination.php
+++ b/Models/Tax/TaxCombination.php
@@ -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 = '';
diff --git a/Theme/Backend/region-analysis.tpl.php b/Theme/Backend/region-analysis.tpl.php
index 2964845..3a3887e 100755
--- a/Theme/Backend/region-analysis.tpl.php
+++ b/Theme/Backend/region-analysis.tpl.php
@@ -787,7 +787,7 @@ echo $this->getData('nav')->render();
?>