Refactored `orderInvoicesRoute` to use a central method for fetching invoice details, reducing code duplication. Added functionality for generating and storing material transaction PDFs using a new HTML2PDF template. Updated database handling for accommodating null filters and enhanced customer data retrieval in orders.
498 lines
19 KiB
PHP
498 lines
19 KiB
PHP
<?php
|
|
|
|
namespace objects;
|
|
|
|
use classes\db;
|
|
use classes\motorapi;
|
|
use classes\object_property;
|
|
use classes\response;
|
|
use Exception;
|
|
use traits\db_object_t;
|
|
|
|
class orders_o extends db
|
|
{
|
|
use db_object_t;
|
|
|
|
public object_property $customer_id;
|
|
public object_property $cashier_id;
|
|
public object_property $reference;
|
|
public object_property $notes;
|
|
public object_property $department_id;
|
|
public object_property $reg_1;
|
|
public object_property $reg_2;
|
|
public object_property $reg_3;
|
|
public economic_module_orders $economic_module_orders;
|
|
public object_property $created_at;
|
|
public object_property $deleted_at;
|
|
|
|
public object_property $completed_at;
|
|
public stripe_module_orders_o $stripe_module_orders;
|
|
public object_property $invoice_collection_id;
|
|
|
|
public function structure(): void
|
|
{
|
|
$this->setTable('orders');
|
|
}
|
|
|
|
public function edit(int $id, int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id): void
|
|
{
|
|
global $db, $response;
|
|
$this->id = $id;
|
|
try {
|
|
// Avoid SQL injection
|
|
$reference = $db->escape_string($reference);
|
|
$notes = $db->escape_string($notes);
|
|
// Update the record in the database
|
|
$sql = "UPDATE $this->table SET customer_id = $customer_id, cashier_id = $cashier_id, reference = '$reference', notes = '$notes', department_id = $department_id WHERE id = $id";
|
|
$db->query($sql);
|
|
|
|
// Set the values of the object properties
|
|
$this->getObjectProperties();
|
|
} catch (Exception $e) {
|
|
$response->error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function getObjectProperties(): void
|
|
{
|
|
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
|
|
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
|
|
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', true);
|
|
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
|
|
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', true);
|
|
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
|
|
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
|
|
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
|
|
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
|
|
$this->completed_at = new object_property($this->table, $this->id, 'completed_at', 'timestamp', false);
|
|
$this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id);
|
|
$this->stripe_module_orders = (new stripe_module_orders_o())->select($this->id);
|
|
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
|
|
$this->invoice_collection_id = new object_property($this->table, $this->id, 'invoice_collection_id', 'int', false);
|
|
}
|
|
|
|
|
|
public function includeIncludes(): orders_o
|
|
{
|
|
global /** @var response $response */
|
|
$response;
|
|
$includeEverything = $response->getRequestParameter('include_all') === 'true';
|
|
/** orderItems */
|
|
if ($response->getRequestParameter('includeOrderItems') || $includeEverything) {
|
|
$response->add_include('orderItems', $this->applyDepartmentPrices($this->getOrderItems($this->id), $this->department_id->value()));
|
|
}
|
|
/** customer */
|
|
if ($response->getRequestParameter('includeCustomer') || $includeEverything) {
|
|
$customer = new users_o();
|
|
$response->add_include('customer', $customer->getOrImportCustomerByCustomerNumber($this->customer_id->value())->includeIncludes()->asArray());
|
|
}
|
|
/** cashier */
|
|
if ($response->getRequestParameter('includeCashier') || $includeEverything) {
|
|
$cashier = new users_o();
|
|
$response->add_include('cashier', $cashier->getUserById($this->cashier_id->value())->asArray());
|
|
}
|
|
/**
|
|
* economicModuleOrders
|
|
*/
|
|
if ($response->getRequestParameter('includeEconomicModuleOrders') || $includeEverything) {
|
|
$response->add_include('economicModuleOrders', $this->economic_module_orders->getByOrderId($this->id)->asArray());
|
|
}
|
|
/**
|
|
* stripeModuleOrders
|
|
*/
|
|
if ($response->getRequestParameter('includeStripeModuleOrders') || $includeEverything) {
|
|
$response->add_include('stripeModuleOrders', $this->stripe_module_orders->exists() ? $this->stripe_module_orders->asArray() : []);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Apply department pricing to a list of products
|
|
* @param array $order_items
|
|
* @param int $department_id
|
|
* @return array
|
|
*/
|
|
public function applyDepartmentPrices(array $order_items, int $department_id): array
|
|
{
|
|
global $response;
|
|
$department = new departments_o();
|
|
$department->getDepartmentById($department_id);
|
|
$department->getDepartmentProductPrices($department_id);
|
|
foreach ( $order_items as $key => $order_item ) {
|
|
$product = new products_o();
|
|
$product->getProductById($order_item['product_id']);
|
|
$order_items[$key]['product']['price'] = $product->getDepartmentPrice($department_id);
|
|
}
|
|
return $order_items;
|
|
}
|
|
|
|
public function getOrderItems(int $order_id): array
|
|
{
|
|
global $db;
|
|
$sql = "SELECT * FROM order_items WHERE order_id = $order_id";
|
|
$result = $db->query($sql);
|
|
$order_items = [];
|
|
if ($result->num_rows > 0 && $result) {
|
|
while ($row = $result->fetch_assoc()) {
|
|
$order_item = new order_items_o();
|
|
$order_items[] = $order_item->getOrderItemById($row['id'])->getItemAsArray();
|
|
}
|
|
}
|
|
return $order_items;
|
|
}
|
|
|
|
public function asArray(): array
|
|
{
|
|
return [
|
|
'id' => $this->id,
|
|
'customer_id' => (int)$this->customer_id->value(),
|
|
'cashier_id' => (int)$this->cashier_id->value(),
|
|
'reference' => $this->reference->value(),
|
|
'notes' => $this->notes->value(),
|
|
'department_id' => (int)$this->department_id->value(),
|
|
'reg_1' => $this->reg_1->value(),
|
|
'reg_2' => $this->reg_2->value(),
|
|
'reg_3' => $this->reg_3->value(),
|
|
'completed_at' => $this->completed_at->value(),
|
|
'created_at' => $this->created_at->value(),
|
|
'deleted_at' => $this->deleted_at->value(),
|
|
'total_net_amount' => $this->getNetAmount(),
|
|
'invoice_collection_id' => (int)$this->invoice_collection_id->value(),
|
|
'closed_at' => (int)$this->invoice_collection_id->value() ? (new collected_order_invoices_o())->select((int)$this->invoice_collection_id->value())->closed_at->value() : null,
|
|
];
|
|
}
|
|
|
|
public function getNetAmount(): float
|
|
{
|
|
self::requireSelected();
|
|
$net = 0;
|
|
// Get the order items object
|
|
$order_items = new order_items_o();
|
|
// Get the price, quantity of the order items
|
|
$items = $order_items->getAllItemsAsArray($this->id);
|
|
// Loop through the items and get the net amount
|
|
foreach ( $items as $item ) {
|
|
$tmp_price = (int)$item['price'];
|
|
$tmp_quantity = (int)$item['quantity'];
|
|
// Add the price to the net amount
|
|
$net += $tmp_price * $tmp_quantity;
|
|
}
|
|
return $net;
|
|
}
|
|
|
|
public function exists(): bool
|
|
{
|
|
// Check if the id is greater than 0, and that the deleted_at property is null
|
|
if ($this->id > 0) {
|
|
$this->getObjectProperties();
|
|
return $this->deleted_at->value() === null;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function getCustomerByOrderId(?string $order_id): users_o
|
|
{
|
|
global $db;
|
|
$sql = "SELECT customer_id FROM orders WHERE id = $order_id";
|
|
$result = $db->query($sql);
|
|
if ($result->num_rows > 0) {
|
|
$row = $result->fetch_assoc();
|
|
return (new users_o())->getCustomerByIdOrCustomerNumber($row['customer_id']);
|
|
}
|
|
return new users_o();
|
|
}
|
|
|
|
public function getCustomerOrdersPaginated(int $customer_number, int $page = 1, int $limit = 10, array $order = ['id' => 'DESC'], string $search = null, array $filters = null): array
|
|
{
|
|
global /** @var response $response */
|
|
$db, $response;
|
|
// Add the customer number to the filters
|
|
$filters['customer_id'] = $customer_number;
|
|
|
|
// List the objects with pagination
|
|
$array = $this->listObjectsWithPagination($page, $limit, $search, $filters, $order);
|
|
|
|
// Get the total number of objects
|
|
$total = $this->getTotalObjects($search, $filters);
|
|
$response->paginate($page, $limit, $total);
|
|
return $array;
|
|
}
|
|
|
|
public function getDepartmentByOrderId($order_id): array
|
|
{
|
|
return (new departments_o())->getDepartmentById($this->department_id->value(), true);
|
|
}
|
|
|
|
public function delete(): void
|
|
{
|
|
// Set the deleted_at property to the current timestamp
|
|
$this->deleted_at->set(date('Y-m-d H:i:s'));
|
|
// Save the object
|
|
}
|
|
|
|
public function restore(): void
|
|
{
|
|
// Set the deleted_at property to null
|
|
$this->deleted_at->set(null);
|
|
// Save the object
|
|
}
|
|
|
|
/**
|
|
* @param string $plate The vehicle plate
|
|
* @param int $entries The number of last entries to return (default 10)
|
|
* @return array The orders for the vehicle plate
|
|
*/
|
|
public function getOrderHistoryByVehiclePlate(string $plate, int $entries = 10): array
|
|
{
|
|
global $db;
|
|
$sql = "SELECT * FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' ORDER BY id DESC LIMIT $entries";
|
|
$result = $db->query($sql);
|
|
return $db->fetch_all($result);
|
|
}
|
|
|
|
public function unlinkOrdersFromInvoiceDraft(int $draftInvoiceNumber): void
|
|
{
|
|
$this->economic_module_orders->unlinkAllOrdersFromDraft($draftInvoiceNumber);
|
|
}
|
|
|
|
public function getOrderCustomer(int $orderId): users_o
|
|
{
|
|
$order = new orders_o();
|
|
$order->getOrderById($orderId);
|
|
return (new users_o())->getCustomerByIdOrCustomerNumber($order->customer_id->value());
|
|
}
|
|
|
|
public function getOrderById(int $id): orders_o
|
|
{
|
|
global $db;
|
|
// Get the record from the database
|
|
$sql = "SELECT * FROM $this->table WHERE id = $id";
|
|
$result = $db->query($sql);
|
|
if ($result->num_rows > 0) {
|
|
$this->id = $id;
|
|
$this->getObjectProperties();
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* This function is used when an order is updated
|
|
* It takes the request data and updates the order accordingly.
|
|
* @return void
|
|
* @example {"id":410,"field":"reg_1","value":"REG12"}
|
|
*/
|
|
public function updateRequest(): void
|
|
{
|
|
global $response;
|
|
// If the permission check is not skipped, require the user to be logged in
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$this->id = $data['id'];
|
|
$this->getObjectProperties();
|
|
// Throw an error if the order does not exist
|
|
if (!$this->exists()) {
|
|
$response->error('Order not found, or already deleted', 400);
|
|
}
|
|
// Validate the field value
|
|
if (!isset($data['field']) || !isset($data['value'])) {
|
|
$response->error('Field and value are required', 400);
|
|
}
|
|
// Check if the value is null
|
|
if ($data['value'] === 'null' || $data['value'] === 'NULL') {
|
|
$this->{$data['field']}->nullify();
|
|
}
|
|
$this->{$data['field']}->set($data['value']);
|
|
}
|
|
|
|
/**
|
|
* Mark the order as completed
|
|
* @throws Exception If the order is not selected
|
|
* @throws Exception If the order is already completed
|
|
*/
|
|
public function markAsCompleted(): void
|
|
{
|
|
global /** @var db $db */
|
|
$db;
|
|
self::requireSelected();
|
|
// Check if the order is already completed
|
|
if ($this->completed_at->value() !== null) {
|
|
throw new Exception('Order is already completed');
|
|
}
|
|
// Set the completed_at property to the current timestamp
|
|
$this->completed_at->set(date('Y-m-d H:i:s'));
|
|
$sql = "UPDATE $this->table SET completed_at = '" . $this->completed_at->value() . "' WHERE id = " . $this->id;
|
|
$db->query($sql);
|
|
$this->objectChanged();
|
|
}
|
|
|
|
public function objectChanged(): void
|
|
{
|
|
// Since the orders object is not cached, there is no need to invalidate the cache
|
|
}
|
|
|
|
/**
|
|
* Get the order by invoice id
|
|
* @param int $invoiceId
|
|
* @return $this
|
|
* @throws Exception
|
|
*/
|
|
public function getOrderByInvoiceId(int $invoiceId): orders_o
|
|
{
|
|
global $db;
|
|
$sql = "SELECT id FROM economic_module_orders WHERE invoice_id = $invoiceId";
|
|
$result = $db->query($sql);
|
|
if ($result->num_rows > 0) {
|
|
$row = $result->fetch_assoc();
|
|
return $this->getOrderById($row['id']);
|
|
}
|
|
throw new Exception('Order not found');
|
|
}
|
|
|
|
/**
|
|
* Set the stripe invoicing for an order
|
|
* @throws Exception
|
|
*/
|
|
public function setStripeInvoicing(string $invoice_id, string $stripe_customer_id, string $url): void
|
|
{
|
|
self::requireSelected();
|
|
$this->stripe_module_orders->add($this->id, $invoice_id, $stripe_customer_id, $url);
|
|
self::objectChanged();
|
|
}
|
|
|
|
public function add(int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id, string $reg_1 = '', string $reg_2 = '', string $reg_3 = ''): orders_o
|
|
{
|
|
global $db, $response;
|
|
try {
|
|
// Avoid SQL injection
|
|
$reference = $db->escape_string($reference);
|
|
$notes = $db->escape_string($notes);
|
|
$reg_1 = $db->escape_string($reg_1);
|
|
$reg_2 = $db->escape_string($reg_2);
|
|
$reg_3 = $db->escape_string($reg_3);
|
|
// Create a new record in the database
|
|
$sql = "INSERT INTO $this->table (customer_id, cashier_id, reference, notes, department_id, reg_1, reg_2, reg_3) VALUES ($customer_id, $cashier_id, '$reference', '$notes', $department_id, '$reg_1', '$reg_2', '$reg_3')";
|
|
$db->query($sql);
|
|
|
|
// Get the id of the new record
|
|
$this->id = $db->insert_id();
|
|
|
|
// Set the values of the object properties
|
|
$this->getObjectProperties();
|
|
self::requireSelected();
|
|
self::assignToInvoiceCollection();
|
|
return $this;
|
|
} catch (Exception $e) {
|
|
$response->error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assign the order to an invoice collection
|
|
* @param int|null $invoiceCollectionId The invoice collection id, if not set, the default invoice collection id will be used.
|
|
* @throws Exception If the order is not selected
|
|
*/
|
|
public function assignToInvoiceCollection(int $invoiceCollectionId = null): void
|
|
{
|
|
// If the invoice collection id is not set, get the default invoice collection id
|
|
self::requireSelected();
|
|
// Get the customer
|
|
$customer = new users_o();
|
|
$customer->getUserByCustomerNumber($this->customer_id->value());
|
|
$customer->requireSelected();
|
|
// Get the invoice collection id
|
|
$invoiceCollectionId = $invoiceCollectionId ?? $customer->getNewOrderInvoiceCollectionId();
|
|
// Assign the order to the invoice collection
|
|
$this->invoice_collection_id->set($invoiceCollectionId);
|
|
self::objectChanged();
|
|
}
|
|
|
|
/**
|
|
* Get the recommended order items, based on the order history, vehicle plate and department
|
|
* @return array The recommended order items
|
|
* @throws Exception If the order is not selected
|
|
*/
|
|
public function getRecommendedOrder(): array
|
|
{
|
|
self::requireSelected();
|
|
$recommended = [
|
|
'reg_1' => self::getRecommendedOrderPlate($this->reg_1->value()),
|
|
'reg_2' => [],
|
|
'reg_3' => [],
|
|
];
|
|
return $recommended;
|
|
}
|
|
|
|
/**
|
|
* @throws Exception If the order is not selected
|
|
*/
|
|
public function getRecommendedOrderPlate(string $plate): array
|
|
{
|
|
self::requireSelected();
|
|
$result = [
|
|
'order_history' => self::get_vehicle_last_orders_items($plate),
|
|
];
|
|
|
|
// If the MotorApi is enabled, get the recommended order items based on the vehicle plate
|
|
$MotorApi = new motorapi();
|
|
if ($MotorApi->config->enabled->isTrue()) {
|
|
$MotorApi_data = $MotorApi->getRecommendedProducts($plate);
|
|
if ($MotorApi_data) {
|
|
// Get the recommended order items based on the vehicle plate
|
|
$result['motorapi'] = $MotorApi_data;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get the last orders item ids for a vehicle plate
|
|
* @param string $plate The vehicle plate
|
|
* @return array The last order item ids
|
|
*/
|
|
public function get_vehicle_last_orders_items(string $plate): array
|
|
{
|
|
global /** @var db $db */
|
|
$db;
|
|
$sql = "SELECT id FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' AND deleted_at IS NULL ORDER BY id DESC LIMIT 5";
|
|
$result = $db->query($sql);
|
|
$orders = $db->fetch_all($result);
|
|
$order_items = [
|
|
1 => [],
|
|
2 => [],
|
|
3 => [],
|
|
4 => [],
|
|
5 => [],
|
|
];
|
|
// Get the order item (ids) from the last 5 orders
|
|
$index = 0;
|
|
foreach ( $orders as $order ) {
|
|
$index++;
|
|
$order_tmp = new orders_o();
|
|
// Get the order items for the order
|
|
$items_tmp = $order_tmp->getOrderItems($order['id']);
|
|
// Add the items to the result
|
|
foreach ( $items_tmp as $item ) {
|
|
$order_items[$index][] = [
|
|
'product_id' => $item['product_id'],
|
|
'reference' => $item['reference'],
|
|
'quantity' => $item['quantity'],
|
|
'product' => $item['product'],
|
|
];
|
|
}
|
|
}
|
|
return $order_items;
|
|
}
|
|
|
|
/**
|
|
* Get the order history for a vehicle plate
|
|
* @param string $plate The vehicle plate
|
|
* @return array The order history
|
|
*/
|
|
public function get_vehicle_order_history(string $plate): array
|
|
{
|
|
global $db;
|
|
$sql = "SELECT * FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' AND deleted_at IS NULL ORDER BY id DESC LIMIT 5";
|
|
$result = $db->query($sql);
|
|
return $db->fetch_all($result);
|
|
}
|
|
} |