Files
api/services/nginx/app/routes/ordersRoute.php
T
Jepp9350 8d2fe8d75d Add support for closed_at field in order invoices
Introduce the closed_at field to track closure dates for order invoices. Updated validation, processing logic, and database integration to ensure correct handling of the new field. Adjusted related routes to pass and store the closed_at value where applicable.
2025-05-01 15:45:59 +02:00

626 lines
29 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\stripe;
use objects\collected_order_invoices_o;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_orders_o;
use objects\stripe_payment_intents_o;
use objects\users_o;
use traits\route_t;
class ordersRoute
{
use route_t;
public function run(): void
{
$this->get('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_orders');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');
// Create economic_module_orders object
$economic_module_orders = new economic_module_orders();
$orders = new orders_o();
$department_ids = $user->getGroup()->getDepartments();
if (self::isParametersSet(['show_wash_subscription'])) {
// Check if the boolean is true
if (self::getParameter('show_wash_subscription') === 'true') {
// add the '10' to the department_ids
$department_ids[] = '10';
}
}
// Return the list of departments
$orders->setView('orders_with_invoice_collections');
$response->success(
$orders->listObjectsWithPaginationIfSet(
function ($order) {
// Add the invoice status to the order
$order['economic_invoice_module'] = (new economic_module_orders())->getByOrderId($order['id'])->asArray();
// Add the total amount to the order
$order['total_net_amount'] = (new orders_o())->select($order['id'])->getNetAmount();
// Add the stripe status to the order
$stripe_module_orders = (new stripe_module_orders_o())->select($order['id']);
if ($stripe_module_orders->exists()) {
$order['stripe_invoice_module'] = $stripe_module_orders->asArray();
}
// If the invoice collection is set, add it to the order
if (!empty($order['invoice_collection_id'])) {
$order['invoice_collection'] = [
'id' => $order['invoice_collection_id'],
'closed_at' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->closed_at->value(),
'booked_invoice_id' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->booked_invoice_id->value() ?? null,
'processor' => (int)(new collected_order_invoices_o())->select($order['invoice_collection_id'])->processor->value() ?? null,
];
}
// Get the customer
$tmp_customer = (new users_o())->getUserByCustomerNumber((int)$order['customer_id']);
// Add the customer name to the order
$order['customer_name'] = (new users_o())->getCustomerName($order['customer_id']);
$order['user_id'] = (int)$tmp_customer->id;
/** @var array $order */
return $order;
},
$orders->forceRestrictFilters(
[
// This makes sure that the user can only see orders from the departments they explicitly have access to
'department_id' => $department_ids,
]
)
)
);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'LIST_ORDERS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_orders' => 'List all orders'
]
);
$this->post('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
$data = $this->getData($data, $response);
// Validate the department
if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
$response->error('Department not found', 400);
}
// Make sure the customer number set is valid
$targetUser = (new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id']);
if (!$targetUser->exists()) {
$response->error('Customer not found', 400);
}
// Check if the user requires a reference
if ($targetUser->requiresReference() && empty($data['reference'])) {
$response->error('Reference is required by the customer', 400);
}
// Get the registration number
$reg_1 = $data['reg_1'];
// Get the registration numbers (If they are set, they 2-3 are optional)
$reg_2 = $data['reg_2'] ?? '';
$reg_3 = $data['reg_3'] ?? '';
// Create the order
$order = (new orders_o())->add((int)$data['customer_id'], $user->id, $data['reference'], $data['notes'], (int)$data['department_id'], (string)$reg_1, (string)$reg_2, (string)$reg_3);
// Log the incident
(new logs_o())->add('orders', $data['department_id'], 1, $user->id, 'ADD_ORDER', 'Successfully added an order (ID: ' . $data['department_id'] . ')');
// Return a success message, containing the orders array
$response->success($order->asArray());
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'ADD_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_order' => 'Add an order'
]
);
$this->put('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('edit_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// If the customer ID is set, validate it
if (isset($data['customer_id'])) {
if (!(new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id'])->exists() || empty($data['customer_id'])) {
$response->error('Customer not found or invalid', 400);
}
$order->customer_id->set((int)$data['customer_id']);
}
// If the reference is set, validate it
if (isset($data['reference'])) {
$order->reference->set($data['reference']);
}
// If the notes are set, validate them
if (isset($data['notes'])) {
$order->notes->set($data['notes']);
}
// If the registration number is set, validate it
if (isset($data['reg_1'])) {
$order->reg_1->set($data['reg_1']);
}
// Check if the invoice collection is set
if (isset($data['invoice_collection_id'])) {
$order->invoice_collection_id->set((int)$data['invoice_collection_id']);
}
// If the department ID is set, validate it
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
// Return a success message
$response->success(['message' => 'Order updated successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'EDIT_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'edit_order' => 'Edit an order'
]
);
$this->delete('/orders', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the payload
$id = $this->fromRequest('id');
// Check if the ID is set
if (!$id) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Delete the order
$order->delete();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_ORDER', 'Successfully deleted an order (ID: ' . $id . ')');
// Return a success message
$response->success(['message' => 'Order deleted successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'DELETE_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_order' => 'Delete an order'
]
);
$this->post('/orders/mark_as_completed', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('mark_order_as_completed');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Mark the order as completed
$order->markAsCompleted();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'MARK_ORDER_AS_COMPLETED', 'Successfully marked an order as completed (ID: ' . $data['id'] . ')');
// Return a success message
$response->success(['message' => 'Order marked as completed successfully']);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'MARK_ORDER_AS_COMPLETED', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'mark_order_as_completed' => 'Mark an order as completed'
]
);
$this->post('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('charge_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Get the department
$department = (new departments_o())->selectId((int)$order->department_id->value());
// Check if the department is configured for Stripe payments
if (!$department->isStripeConfigured()) {
$response->error('Department is not configured for Stripe payments', 400);
}
// Check if the reader is set
if (!isset($data['reader'])) {
$response->error('Reader ID is required', 400);
}
// Get the tax percentage (if any)
$tax_percentage = (isset($data['tax_percentage'])) ? (int)$data['tax_percentage'] : null;
// Check if the tax percentage is valid
if ($tax_percentage !== null && ($tax_percentage < 0 || $tax_percentage > 100)) {
$response->error('Invalid tax percentage', 400);
}
function addTaxNetAmount($net_amount, $tax_percentage): float
{
// Check if the tax percentage is above 0
if (empty($tax_percentage) || $tax_percentage <= 0) {
return $net_amount;
}
return $net_amount + ($net_amount * ($tax_percentage / 100));
}
// Get the Stripe payment intent
$stripe = new stripe();
$paymentIntent = $stripe->payment_intents->create(
addTaxNetAmount(
(float)$order->getNetAmount() * 100,
$tax_percentage ?? 0
),
[
'description' => 'Order ID: ' . $order->id,
'metadata' => [
'order_id' => $order->id,
'customer_id' => $order->customer_id->value(),
'department_id' => $order->department_id->value(),
'tax_percentage' => $tax_percentage ?? 0,
],
'payment_method_types' => ['card_present'],
'capture_method' => 'manual',
]
);
// Validate the payment intent
try {
$stripe->payment_intents->get($paymentIntent->id);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
// Set the payment intent ID in the order
$stripe_payment_intents = new stripe_payment_intents_o();
$stripe_payment_intents->add(
(int)$order->id,
$paymentIntent->id,
$paymentIntent->client_secret,
$paymentIntent->toJSON()
);
// Send the payment intent to the reader
$stripe->readers->sendPaymentIntent(
$data['reader'],
$paymentIntent->id,
);
// Set the reader on the stripe payment intent
$stripe_payment_intents->reader_id->set($data['reader']);
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CHARGE_ORDER', 'Successfully charged an order (ID: ' . $data['id'] . ')');
$response->success([
'payment_intent' => $paymentIntent->id,
'client_secret' => $paymentIntent->client_secret,
]);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'CHARGE_ORDER', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'charge_order' => 'Charge an order'
]
);
$this->get('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('get_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
self::requireParameters([
'id'
]);
// Check if the required fields are set
$id = self::getParameter('id');
if (!isset($id)) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Get the Stripe payment intent
$stripe = new stripe();
try {
$payment_intent = $stripe->payment_intents->get(
$stripe_payment_intents->payment_intent_id->value(),
[
//'expand' => ['latest_charge'], // This is used to get the latest charge, that can be used to check if the payment has been refunded.
]
);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
$response->success(
$payment_intent->toJSON()
);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'GET_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'get_payment_intent' => 'Get a payment intent'
]
);
$this->delete('/orders/module/stripe/payment_intent', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('charge_order');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the data
self::requireParameters([
'id'
]);
$id = self::fromRequest('id');
// Get the current order
$order = (new orders_o())->getOrderById((int)$id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
try {
$stripe_payment_intents->delete();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_PAYMENT_INTENT', 'Successfully deleted a payment intent (ID: ' . $id . ')');
// Return a success message
$response->success(['message' => 'Payment intent deleted successfully']);
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'DELETE_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'charge_order' => 'Delete a payment intent'
]
);
$this->post('/orders/module/stripe/payment_intent/capture', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('confirm_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Confirm the payment intent
$stripe = new stripe();
try {
$paymentIntent = $stripe->payment_intents->capture(
$stripe_payment_intents->payment_intent_id->value(),
[] // Since we are capturing the payment, we don't need to pass any data
);
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CONFIRM_PAYMENT_INTENT', 'Successfully confirmed a payment intent (ID: ' . $data['id'] . ')');
// Check if the payment intent was successful
if ($paymentIntent->status !== 'succeeded') {
$response->error('Payment intent not successful', 400);
} else {
// Update the order collection to reflect the payment
$order_collection = $order->getOrderCollection();
$order_collection->paidWithStripe($paymentIntent->id);
}
// Return a success message
$response->success($paymentIntent->toJSON());
} catch (\Stripe\Exception\InvalidRequestException $e) {
$response->error('Payment intent not found', 400);
}
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'CONFIRM_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'confirm_payment_intent' => 'Confirm a payment intent'
]
);
$this->post('/orders/module/stripe/debug/simulate_payment', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('debug_simulate_payment_intent');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
// Get the current order
$order = (new orders_o())->getOrderById((int)$data['id']);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 400);
}
// Check if the order has a payment intent
$stripe_payment_intents = new stripe_payment_intents_o();
if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
$response->error('Order does not have a payment intent', 400);
}
$stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
// Simulate the payment
$stripe = new stripe();
//$paymentIntent = $stripe->readers->simulatePayment(
// $stripe_payment_intents->payment_intent_id->value(),
//);
$response->success('TEST_SUCCESS', 200);
} else {
// Log the incident
(new logs_o())->add('orders', 'global', 1, 0, 'SIMULATE_PAYMENT_INTENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'simulate_payment_intent' => 'Simulate a payment intent, this is only for testing purposes and should under no circumstances be used in production'
]
);
}
/**
* @param mixed $data
* @param response $response
* @return mixed
*/
private function getData(mixed $data, response $response): mixed
{
if (!isset($data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if ((int)$data['customer_id'] < 1 || !is_numeric((int)$data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if (!isset($data['department_id'])) {
$response->error('Department ID is required', 400);
}
if (!isset($data['reference'])) {
$response->error('Reference is required', 400);
}
if (!isset($data['notes'])) {
$response->error('Notes is required', 400);
}
if (!isset($data['reg_1'])) {
$response->error('Registration number 1 is required', 400);
}
if (strlen($data['reg_1']) < 4) {
$response->error('Registration number 1 must be at least 4 characters', 400);
}
// Optional fields are not checked here, as they are optional and can be empty
return $data;
}
}