Added functionality to handle vehicle subscriptions in invoices, including validation and item visibility. Introduced department notification SMS capability with endpoints for creating, retrieving, and deleting SMS records. Enhanced invoice drafting logic to exclude items not flagged for inclusion in invoices.
515 lines
19 KiB
PHP
515 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())->getUserByCustomerNumber($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);
|
|
}
|
|
|
|
/**
|
|
* @throws Exception If the order is not selected
|
|
*/
|
|
public function getIncludeInInvoiceCount(): int
|
|
{
|
|
self::requireSelected();
|
|
$order_items = new order_items_o();
|
|
$items = $order_items->getAllItemsAsArray($this->id);
|
|
$count = 0;
|
|
foreach ( $items as $item ) {
|
|
if ($item['include_in_invoice']) {
|
|
$count++;
|
|
}
|
|
}
|
|
return $count;
|
|
}
|
|
} |