438 lines
17 KiB
PHP
438 lines
17 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
use economic_invoice_draft_mo;
|
|
use Exception;
|
|
use objects\collected_order_invoices_o;
|
|
use objects\customer_fixed_pricing_o;
|
|
use objects\departments_o;
|
|
use objects\economic_module_orders;
|
|
use objects\logs_o;
|
|
use objects\orders_o;
|
|
use objects\users_o;
|
|
|
|
/**
|
|
* Executes e-conomic transfer flows that are now run by queue workers.
|
|
*/
|
|
class economic_transfer_executor
|
|
{
|
|
/**
|
|
* Export order draft invoice to e-conomic.
|
|
* @throws Exception
|
|
*/
|
|
public function exportOrderDraftInvoice(int $order_id, int $user_id = 0): array
|
|
{
|
|
$order = (new orders_o())->getOrderById($order_id);
|
|
if (!$order->exists()) {
|
|
throw new Exception('Order not found');
|
|
}
|
|
|
|
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
|
|
$invoice_id = (int)$economic_module_orders->economic_invoice_id->value();
|
|
if ($invoice_id > 0) {
|
|
throw new Exception('An invoice has already been created, invoice ID: ' . $invoice_id);
|
|
}
|
|
|
|
$order_items = (new orders_o())->getOrderItems($order_id);
|
|
$order_items = (new orders_o())->applyDepartmentPrices($order_items, $order->department_id->value());
|
|
if (count($order_items) === 0) {
|
|
throw new Exception('No order items found');
|
|
}
|
|
|
|
$customer = (new orders_o())->getCustomerByOrderId($order_id);
|
|
if (!$customer->exists()) {
|
|
throw new Exception('Customer not found');
|
|
}
|
|
|
|
$customer_economic = $customer->getCustomerEcocomicData()->economic_customer;
|
|
$economic_invoice_draft = new economic_invoice_draft_mo();
|
|
$economic_invoice_draft->setCustomerNumber((int)$customer_economic->customer_number);
|
|
$economic_invoice_draft->setRecipient(
|
|
$customer_economic->name ?? 'Ukendt',
|
|
$customer_economic->address ?? 'Ukendt',
|
|
$customer_economic->zip ?? 'Ukendt',
|
|
$customer_economic->city ?? 'Ukendt'
|
|
);
|
|
|
|
$department = (new departments_o())->getDepartmentById($order->department_id->value());
|
|
$this->addTheDepartmentDateReference($economic_invoice_draft, $department['name'], $order);
|
|
|
|
$billable_order_items = 0;
|
|
foreach ($order_items as $order_item) {
|
|
$added = $this->addOrderItemToInvoice(
|
|
$customer,
|
|
$order,
|
|
$order_item,
|
|
$economic_invoice_draft,
|
|
(int)($order_item['quantity'] ?? 1)
|
|
);
|
|
if ($added) {
|
|
$billable_order_items++;
|
|
}
|
|
}
|
|
|
|
if ($billable_order_items < 1) {
|
|
throw new Exception('No billable order items found');
|
|
}
|
|
|
|
$result = null;
|
|
if ($customer->hasOpenInvoiceDraft() && !$customer->invoicePerOrder()) {
|
|
$open_invoice_draft = (int)$customer->getOpenInvoiceDraft();
|
|
$result = $this->addOrderToInvoiceDraft($open_invoice_draft, $order, $customer, $order_items);
|
|
}
|
|
if ($result === null) {
|
|
$result = $economic_invoice_draft->createInvoiceDraftExample();
|
|
}
|
|
|
|
if (!isset($result->draftInvoiceNumber) && !isset($result->lines[0])) {
|
|
(new logs_o())->add(
|
|
'economic_invoice_draft',
|
|
'global',
|
|
3,
|
|
$user_id,
|
|
'ECONOMIC_INVOICE_DRAFT_EXPORT',
|
|
'Failed to create economic invoice draft'
|
|
);
|
|
$message = $result->message ?? 'Failed to create economic invoice draft';
|
|
throw new Exception((string)$message);
|
|
}
|
|
|
|
if ($economic_module_orders->economic_invoice_draft_id->value() > 0) {
|
|
$economic_invoice_draft->deleteInvoiceDraft($economic_module_orders->economic_invoice_draft_id->value());
|
|
}
|
|
|
|
$new_draft_id = (int)($result->draftInvoiceNumber ?? $customer->getOpenInvoiceDraft());
|
|
$economic_module_orders->economic_invoice_draft_id->set($new_draft_id);
|
|
if (!$customer->invoicePerOrder()) {
|
|
$customer->setOpenInvoiceDraft($new_draft_id);
|
|
} else {
|
|
$customer->unsetOpenInvoiceDraft();
|
|
}
|
|
|
|
(new logs_o())->add(
|
|
'economic_invoice_draft',
|
|
'global',
|
|
1,
|
|
$user_id,
|
|
'ECONOMIC_INVOICE_DRAFT_EXPORT',
|
|
'Successfully exported an economic invoice draft'
|
|
);
|
|
|
|
return $economic_module_orders->getArray();
|
|
}
|
|
|
|
/**
|
|
* Export booked invoice from existing draft.
|
|
* @throws Exception
|
|
*/
|
|
public function exportOrderInvoice(int $order_id, int $user_id = 0): array
|
|
{
|
|
$order = (new orders_o())->getOrderById($order_id);
|
|
if (!$order->exists()) {
|
|
throw new Exception('Order not found');
|
|
}
|
|
|
|
$customer = (new orders_o())->getCustomerByOrderId($order_id);
|
|
if (!$customer->exists()) {
|
|
throw new Exception('Customer not found');
|
|
}
|
|
|
|
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
|
|
if ($economic_module_orders->economic_invoice_draft_id->value() === 0) {
|
|
throw new Exception('No economic invoice draft found');
|
|
}
|
|
|
|
$invoice_id = (int)$economic_module_orders->economic_invoice_id->value();
|
|
if ($invoice_id > 0) {
|
|
throw new Exception('An invoice has already been created, invoice ID: ' . $invoice_id);
|
|
}
|
|
|
|
$invoice_draft_id = (int)$economic_module_orders->economic_invoice_draft_id->value();
|
|
$economic_invoice_draft = new economic_invoice_draft_mo();
|
|
$result = $economic_invoice_draft->publishInvoiceDraft($invoice_draft_id);
|
|
|
|
if (!isset($result->bookedInvoiceNumber)) {
|
|
(new logs_o())->add(
|
|
'economic_invoice',
|
|
'global',
|
|
3,
|
|
$user_id,
|
|
'ECONOMIC_INVOICE_EXPORT',
|
|
'Failed to create economic invoice from draft: ' . $invoice_draft_id
|
|
);
|
|
$message = $result->message ?? 'Failed to create economic invoice';
|
|
throw new Exception((string)$message);
|
|
}
|
|
|
|
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
|
|
$economic_module_orders->economic_invoice_id->set((int)$result->bookedInvoiceNumber);
|
|
$customer->unsetOpenInvoiceDraft();
|
|
|
|
(new logs_o())->add(
|
|
'economic_invoice',
|
|
'global',
|
|
1,
|
|
$user_id,
|
|
'ECONOMIC_INVOICE_EXPORT',
|
|
'Successfully exported an economic invoice'
|
|
);
|
|
|
|
return $economic_module_orders->getArray();
|
|
}
|
|
|
|
/**
|
|
* Export collected invoice to e-conomic.
|
|
* @throws Exception
|
|
*/
|
|
public function exportCollectedInvoice(int $collected_invoice_id, bool $send_as_is = false, int $user_id = 0): array
|
|
{
|
|
$collected_order_invoices = (new collected_order_invoices_o())->select($collected_invoice_id);
|
|
$collected_order_invoices->requireSelected();
|
|
|
|
if ($collected_order_invoices->external_id->value() === null) {
|
|
if (!$send_as_is) {
|
|
$customer_fixed_pricing_o = new customer_fixed_pricing_o();
|
|
if ($customer_fixed_pricing_o->doesUserHaveFixedPricing((int)$collected_order_invoices->customer_number->value())) {
|
|
$customer_fixed_pricing_price = $customer_fixed_pricing_o->selectByCustomerNumber((int)$collected_order_invoices->customer_number->value());
|
|
$customer_fixed_pricing_price = (int)$customer_fixed_pricing_price->price->value();
|
|
$collected_order_invoices->overridePricesFixed($customer_fixed_pricing_price);
|
|
} else {
|
|
$collected_order_invoices->addVehicleSubscriptionsTransaction();
|
|
}
|
|
} else {
|
|
$collected_order_invoices->removeSpecialArrangements();
|
|
$collected_order_invoices->setAllItemsToBeIncludedInInvoice();
|
|
}
|
|
|
|
$collected_order_invoices->addToEconomic();
|
|
} else {
|
|
if ($collected_order_invoices->booked_invoice_id->value() !== null) {
|
|
throw new Exception('Invoice has already been booked');
|
|
}
|
|
if ($collected_order_invoices->isDraftExisting()) {
|
|
throw new Exception('Invoice draft already exists in E-Conomic');
|
|
}
|
|
|
|
$customer_fixed_pricing_o = new customer_fixed_pricing_o();
|
|
if ($customer_fixed_pricing_o->doesUserHaveFixedPricing((int)$collected_order_invoices->customer_number->value())) {
|
|
$customer_fixed_pricing_price = $customer_fixed_pricing_o->selectByCustomerNumber((int)$collected_order_invoices->customer_number->value());
|
|
$customer_fixed_pricing_price = (int)$customer_fixed_pricing_price->price->value();
|
|
$collected_order_invoices->overridePricesFixed($customer_fixed_pricing_price);
|
|
} else {
|
|
$collected_order_invoices->addVehicleSubscriptionsTransaction();
|
|
}
|
|
|
|
$collected_order_invoices->addToEconomic(true);
|
|
}
|
|
|
|
(new logs_o())->add(
|
|
'orderInvoices',
|
|
'global',
|
|
1,
|
|
$user_id,
|
|
'ADD_COLLECTED_INVOICE_ECONOMIC',
|
|
'Queued transfer processed successfully for collected invoice #' . $collected_invoice_id
|
|
);
|
|
|
|
return $collected_order_invoices->asArray();
|
|
}
|
|
|
|
/**
|
|
* @throws Exception
|
|
*/
|
|
public function addTheDepartmentDateReference(economic_invoice_draft_mo $economic_invoice_draft, mixed $department_name, orders_o $order): void
|
|
{
|
|
$parsed_date = date('d/m/Y H:i', strtotime($order->created_at->value()));
|
|
$economic_invoice_draft->addLineTEXT("[ " . $parsed_date . ' ' . $department_name . ' #' . $order->id . " ]");
|
|
if ($order->reference->value() !== '') {
|
|
$economic_invoice_draft->addLineTEXT('Reference:');
|
|
if (str_contains($order->reference->value(), "\n")) {
|
|
foreach (explode("\n", $order->reference->value()) as $line) {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $line);
|
|
}
|
|
} else {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $order->reference->value());
|
|
}
|
|
}
|
|
|
|
$line_reg = '';
|
|
if ($order->reg_1->value() !== '') {
|
|
$line_reg .= 'Reg 1: ' . strtoupper($order->reg_1->value());
|
|
}
|
|
if ($order->reg_2->value() !== '') {
|
|
$line_reg .= ', Reg 2: ' . strtoupper($order->reg_2->value());
|
|
}
|
|
if ($order->reg_3->value() !== '') {
|
|
$line_reg .= ', Reg 3: ' . strtoupper($order->reg_3->value());
|
|
}
|
|
$economic_invoice_draft->addLineTEXT($line_reg);
|
|
|
|
if ($order->notes->value() !== '') {
|
|
$economic_invoice_draft->addLineTEXT('Notat:');
|
|
if (str_contains($order->notes->value(), "\n")) {
|
|
foreach (explode("\n", $order->notes->value()) as $line) {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $line);
|
|
}
|
|
} else {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $order->notes->value());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true when a product line was added, false when skipped.
|
|
* @throws Exception
|
|
*/
|
|
public function addOrderItemToInvoice(
|
|
users_o $customer,
|
|
orders_o $order,
|
|
mixed $order_item,
|
|
economic_invoice_draft_mo $economic_invoice_draft,
|
|
int $quantity = 1
|
|
): bool {
|
|
if (self::shouldSkipOrderItemForInvoice($order_item, $quantity)) {
|
|
return false;
|
|
}
|
|
|
|
if (!is_array($order_item)) {
|
|
throw new Exception('Order item payload must be an array');
|
|
}
|
|
|
|
$product_number = trim((string)($order_item['product']['economic_product_id'] ?? ''));
|
|
if ($product_number === '') {
|
|
throw new Exception('Order item is missing economic product id');
|
|
}
|
|
|
|
$product_name = trim((string)($order_item['product']['name'] ?? ''));
|
|
if ($product_name === '') {
|
|
$product_name = 'Ukendt produkt';
|
|
}
|
|
|
|
$reference = isset($order_item['reference']) ? (string)$order_item['reference'] : '';
|
|
$notes = isset($order_item['notes']) ? (string)$order_item['notes'] : '';
|
|
|
|
$department = $order->getDepartmentByOrderId($order->id);
|
|
$economic_department_id = $department['economic_department_id'];
|
|
$economic_dimension_id = $department['economic_dimension_id'] ?? 0;
|
|
$order_item_price = (float)($order_item['price'] ?? 0);
|
|
$product_price = (float)($order_item['product']['price'] ?? 0);
|
|
|
|
$economic_invoice_draft->addLine(
|
|
$product_number,
|
|
$product_name,
|
|
$quantity,
|
|
$order_item_price,
|
|
0,
|
|
(int)$economic_department_id ?? 0,
|
|
(int)$economic_dimension_id ?? 0
|
|
);
|
|
|
|
$show_discount = abs($order_item_price - $product_price) > 0.00001;
|
|
if ($show_discount && abs($product_price) > 0.00001) {
|
|
$discount_percentage = round((($product_price - $order_item_price) / $product_price) * 100, 0);
|
|
$economic_invoice_draft->addLineTEXT('Rabat: ' . ($order_item_price - $product_price) . ' DKK (' . $discount_percentage . '%)');
|
|
}
|
|
|
|
if ($reference !== '') {
|
|
$economic_invoice_draft->addLineTEXT('Reference:');
|
|
if (str_contains($reference, "\n")) {
|
|
foreach (explode("\n", $reference) as $line) {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $line);
|
|
}
|
|
} else {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $reference);
|
|
}
|
|
}
|
|
|
|
if ($customer->doesUserHaveAttribute('requiresRegistrationNumbersInvoice')) {
|
|
$line_reg = '';
|
|
if ($order->reg_1->value() !== '') {
|
|
$line_reg .= 'Reg 1: ' . strtoupper($order->reg_1->value());
|
|
}
|
|
if ($order->reg_2->value() !== '') {
|
|
$line_reg .= ', Reg 2: ' . strtoupper($order->reg_2->value());
|
|
}
|
|
if ($order->reg_3->value() !== '') {
|
|
$line_reg .= ', Reg 3: ' . strtoupper($order->reg_3->value());
|
|
}
|
|
$economic_invoice_draft->addLineTEXT($line_reg);
|
|
}
|
|
|
|
if ($notes !== '') {
|
|
$economic_invoice_draft->addLineTEXT('Notat:');
|
|
if (str_contains($notes, "\n")) {
|
|
foreach (explode("\n", $notes) as $line) {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $line);
|
|
}
|
|
} else {
|
|
$economic_invoice_draft->addLineTEXT('# ' . $notes);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @throws Exception
|
|
*/
|
|
public function addOrderToInvoiceDraft(int $economic_invoice_draft_id, orders_o $order, users_o $customer, array $order_items): object
|
|
{
|
|
$has_billable_items = false;
|
|
foreach ($order_items as $order_item) {
|
|
if (!self::shouldSkipOrderItemForInvoice($order_item, (int)($order_item['quantity'] ?? 1))) {
|
|
$has_billable_items = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$has_billable_items) {
|
|
throw new Exception('No billable order items found');
|
|
}
|
|
|
|
$economic_invoice_draft = new economic_invoice_draft_mo();
|
|
$economic_invoice_draft->addLineTEXT('');
|
|
$economic_invoice_draft->addLineTEXT('');
|
|
$this->addTheDepartmentDateReference($economic_invoice_draft, $order->getDepartmentByOrderId($order->id)['name'], $order);
|
|
|
|
foreach ($order_items as $order_item) {
|
|
$this->addOrderItemToInvoice($customer, $order, $order_item, $economic_invoice_draft, (int)($order_item['quantity'] ?? 1));
|
|
}
|
|
|
|
return $economic_invoice_draft->addLinesToInvoiceDraft($economic_invoice_draft_id);
|
|
}
|
|
|
|
/**
|
|
* Skip item lines that are not billable in e-conomic export (0 quantity or 0 unit cost).
|
|
*/
|
|
public static function shouldSkipOrderItemForInvoice(mixed $order_item, int $quantity = 1): bool
|
|
{
|
|
if (!is_array($order_item)) {
|
|
return true;
|
|
}
|
|
|
|
$line_quantity = $quantity;
|
|
if (isset($order_item['quantity']) && is_numeric($order_item['quantity'])) {
|
|
$line_quantity = (int)$order_item['quantity'];
|
|
}
|
|
|
|
if ($line_quantity <= 0) {
|
|
return true;
|
|
}
|
|
|
|
$line_price = null;
|
|
if (isset($order_item['price']) && is_numeric($order_item['price'])) {
|
|
$line_price = (float)$order_item['price'];
|
|
}
|
|
|
|
if ($line_price === null) {
|
|
return true;
|
|
}
|
|
|
|
if (abs($line_price) < 0.00001) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|