Refactor InvoicingPeriodRoute and vehiclesRoute: streamline invoicing period data retrieval, enhance transaction handling across customer types, introduce new methods for efficient object construction, and add improved type validation in object_property.

This commit is contained in:
Jepp9350
2025-06-30 14:37:37 +02:00
parent e0f582289f
commit 6b80637208
4 changed files with 224 additions and 128 deletions
@@ -46,12 +46,15 @@ class object_property
throw new \InvalidArgumentException("Table or column cannot be empty"); throw new \InvalidArgumentException("Table or column cannot be empty");
} }
// Check if the type is valid // Check if the type is valid
if (!in_array($this->type, ['int', 'varchar', 'text', 'date', 'datetime', 'string', 'timestamp'])) { if (!in_array($this->type, ['int', 'varchar', 'text', 'date', 'datetime', 'string', 'timestamp', 'boolean', 'bool'])) {
throw new \InvalidArgumentException("Invalid type: $this->type"); throw new \InvalidArgumentException("Invalid type: $this->type");
} }
$sql = "SELECT $this->column FROM $this->table WHERE id = $this->id"; $sql = "SELECT $this->column FROM $this->table WHERE id = $this->id";
$result = $db->query($sql); $result = $db->query($sql);
$row = $db->fetch_assoc($result); $row = $db->fetch_assoc($result);
if (empty($row)) {
throw new \RuntimeException("No record found for column '$this->column' in table '$this->table' with ID $this->id");
}
return $row[$this->column]; return $row[$this->column];
} }
+20
View File
@@ -1239,4 +1239,24 @@ class users_o extends db
return !empty($this->xlvask_customer_id->value()); return !empty($this->xlvask_customer_id->value());
} }
public function getCustomersWithSpecialArrangements(): array
{
global $db;
$sql = "SELECT DISTINCT c.customer_number
FROM users c
INNER JOIN user_key_value_pairs kv ON c.id = kv.user_id
WHERE kv.var = 'OtherSpecialArrangement'
AND kv.val IS NOT NULL
AND kv.val != ''";
$result = $db->query($sql);
$customer_numbers = [];
while ($row = $result->fetch_assoc()) {
$customer_numbers[] = (int)$row['customer_number'];
}
return $customer_numbers;
}
} }
+197 -127
View File
@@ -7,6 +7,7 @@ use Exception;
use objects\customer_vehicles_o; use objects\customer_vehicles_o;
use objects\logs_o; use objects\logs_o;
use objects\orders_o; use objects\orders_o;
use objects\users_o;
use traits\route_t; use traits\route_t;
class InvoicingPeriodRoute class InvoicingPeriodRoute
@@ -33,6 +34,8 @@ class InvoicingPeriodRoute
// Require the dateFrom and dateTo parameters to be valid dates // Require the dateFrom and dateTo parameters to be valid dates
self::requireDateFormat($dateFrom, 'Y-m-d'); self::requireDateFormat($dateFrom, 'Y-m-d');
self::requireDateFormat($dateTo, 'Y-m-d'); self::requireDateFormat($dateTo, 'Y-m-d');
// Add a day to the dateTo parameter to include the end date in the range
$dateTo = date('Y-m-d', strtotime($dateTo . ' +1 day'));
// Get the invoicing period for the user // Get the invoicing period for the user
$response->success([...self::getInvoicingPeriod($dateFrom, $dateTo)]); $response->success([...self::getInvoicingPeriod($dateFrom, $dateTo)]);
// Log the incident // Log the incident
@@ -68,14 +71,17 @@ class InvoicingPeriodRoute
*/ */
private static function getInvoicingPeriod(string $dateFrom, string $dateTo): array private static function getInvoicingPeriod(string $dateFrom, string $dateTo): array
{ {
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
return [ return [
'dateFrom' => $dateFrom, 'dateFrom' => $dateFrom,
'dateTo' => $dateTo, 'dateTo' => $dateTo,
'types' => [ 'types' => [
'vehicle_subscriptions' => self::getVehicleSubscriptions($dateFrom, $dateTo), 'vehicle_subscriptions' => self::getVehicleSubscriptions($dateFrom, $dateTo, $customersWithTransactions),
'fixed_pricing' => self::getFixedPricing($dateFrom, $dateTo), 'fixed_pricing' => self::getFixedPricing($dateFrom, $dateTo, $customersWithTransactions),
'tank_cleaning' => self::getTankCleaning($dateFrom, $dateTo), 'tank_cleaning' => self::getTankCleaning($dateFrom, $dateTo, $customersWithTransactions),
'all' => self::getCustomersWithTransactions($dateFrom, $dateTo), 'special_arrangements' => self::getSpecialArrangements($dateFrom, $dateTo, $customersWithTransactions),
'invoice_per_order' => self::getInvoicingPerOrder($dateFrom, $dateTo, $customersWithTransactions),
'all' => $customersWithTransactions,
], ],
]; ];
} }
@@ -83,48 +89,134 @@ class InvoicingPeriodRoute
/** /**
* @throws Exception * @throws Exception
*/ */
private static function getVehicleSubscriptions(string $dateFrom, string $dateTo): array private static function getCustomersWithTransactions(string $dateFrom, string $dateTo): array
{ {
// Get all customers with vehicle subscriptions // Define the customers with orders in the specified date range
$customers = (new orders_o)->getCustomersWithOrdersInDateRange($dateFrom, $dateTo);
$customer_numbers_processed = [];
$tmp = [];
foreach ( $customers as $customer ) {
if (in_array((int)$customer->customer_number->value(), $customer_numbers_processed)) {
// Skip if the customer has already been processed
continue;
}
$customer_numbers_processed[] = (int)$customer->customer_number->value();
// Construct the customer object with transactions
$tmp[] = self::constructCustomerObject(
(int)$customer->customer_number->value(),
(new \objects\users_o())->getCustomerName((int)$customer->customer_number->value()),
(new \objects\orders_o())->getTransactionsForCustomer(
(int)$customer->customer_number->value(),
$dateFrom,
$dateTo
),
false,
(int)$customer->id
);
}
return $tmp;
}
/**
* @param int $customer_number
* @param string $customer_name
* @param orders_o[] $transactions
* @param bool $requires_action
* @return array
* @throws Exception
*/
protected static function constructCustomerObject(
int $customer_number,
string $customer_name,
array $transactions = [],
bool $requires_action = false,
?int $user_id = null
): array
{
return [
'id' => $user_id ?? (new users_o())->getUserByCustomerNumber((int)$customer_number)->id,
'customer_number' => $customer_number,
'customer_name' => $customer_name,
'transactions' => $parsed_transactions = array_map(function ($transaction) {
return self::constructTransactionObject($transaction);
}, $transactions),
'requires_action' => self::checkRequiresAction($parsed_transactions, $requires_action),
];
}
/**
* @throws Exception
*/
private static function constructTransactionObject(orders_o $transaction): array
{
return [
'id' => $transaction->id,
'date' => $transaction->created_at->value(),
'amount' => $transaction->getNetAmount(),
'booked' => $transaction->isBooked(),
];
}
private static function checkRequiresAction(array $parsed_transactions, bool $requires_action): bool
{
// If requires_action is already set to true, return true
if ($requires_action) {
return true;
}
// Check if any transaction is not booked
foreach ( $parsed_transactions as $transaction ) {
if (!$transaction['booked']) {
return true;
}
}
// If all transactions are booked, return false
return false;
}
/**
* @throws Exception
*/
private static function getVehicleSubscriptions(string $dateFrom, string $dateTo, array $customersWithTransactions = null): array
{
// If customersWithTransactions is not provided, get all customers with transactions in the specified date range
if ($customersWithTransactions === null) {
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
}
// Since these are monthly subscriptions, we don't need to filter by transactions
$customer_numbers = (new \objects\users_o())->getCustomersWithVehicleSubscriptions(); $customer_numbers = (new \objects\users_o())->getCustomersWithVehicleSubscriptions();
// Get all customers with vehicle subscriptions
$subscriptions = []; $subscriptions = [];
/** @var int $customer_number */ /** @var int $customer_number */
foreach ( $customer_numbers as $customer_number ) { foreach ( $customer_numbers as $customer_number ) {
$tmp_subscription = [ $subscriptions[] = (self::getCustomerFromList((int)$customer_number, $customersWithTransactions)) ?? self::constructCustomerObject(
'customer_number' => $customer_number, (int)$customer_number,
'customer_name' => (new \objects\users_o())->getCustomerName($customer_number), (new \objects\users_o())->getCustomerName((int)$customer_number) ?? 'Unknown Customer',
'transactions' => [], [],
'requires_action' => true, true
]; );
// Check if the customer has a vehicle subscription order within the date range
$orders = (new \objects\orders_o())->getWashSubscriptionTransactions($customer_number, false, $dateFrom, $dateTo);
/** @var orders_o $order */
foreach ( $orders as $order ) {
//echo "Checking order {$order->id} for customer {$customer_number}...\n";
// Add the order to the subscription transactions
$tmp_subscription['transactions'][] = [
'id' => $order->id,
];
}
// If there are transactions, set requires_action to false
if (count($tmp_subscription['transactions']) > 0) {
// Check if there's any transaction that has not been booked yet
$requires_action = false;
foreach ( $tmp_subscription['transactions'] as $transaction ) {
$order = (new orders_o())->select((int)$transaction['id']);
if (!$order->isBooked()) {
$requires_action = true;
break;
}
}
$tmp_subscription['requires_action'] = $requires_action;
}
// Add the subscription to the list if it has transactions
$subscriptions[] = $tmp_subscription;
} }
return $subscriptions; return $subscriptions;
} }
/**
* Get the customer from the list of customers with transactions.
*
* @param int $customer_number The customer number to search for.
* @param array $customersWithTransactions The list of customers with transactions.
* @return array|null The customer object if found, null otherwise.
*/
private static function getCustomerFromList(int $customer_number, array $customersWithTransactions): ?array
{
// Search for the customer in the list of customers with transactions
foreach ( $customersWithTransactions as $customer ) {
if ($customer['customer_number'] === $customer_number) {
return $customer;
}
}
// If the customer is not found, return null
return null;
}
/** /**
* @throws Exception * @throws Exception
*/ */
@@ -137,7 +229,7 @@ class InvoicingPeriodRoute
foreach ( $customer_numbers as $customer_number ) { foreach ( $customer_numbers as $customer_number ) {
$tmp_fixed_pricing = [ $tmp_fixed_pricing = [
'customer_number' => $customer_number, 'customer_number' => $customer_number,
'customer_name' => (new \objects\users_o())->getCustomerName($customer_number), 'customer_name' => (new \objects\users_o())->getCustomerName($customer_number) ?? 'Unknown Customer',
'transactions' => [], 'transactions' => [],
'requires_action' => true, 'requires_action' => true,
]; ];
@@ -173,119 +265,97 @@ class InvoicingPeriodRoute
/** /**
* @throws Exception * @throws Exception
*/ */
private static function getTankCleaning(string $dateFrom, string $dateTo): array private static function getTankCleaning(string $dateFrom, string $dateTo, array $customersWithTransactions = null): array
{ {
// Get all customers with tank cleaning // If customersWithTransactions is not provided, get all customers with transactions in the specified date range
if ($customersWithTransactions === null) {
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
}
// Filter out customers that do not have any transactions in the specified date range
$customer_numbers = (new \objects\users_o())->getCustomersWithTankCleaning(); $customer_numbers = (new \objects\users_o())->getCustomersWithTankCleaning();
self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
// Get all customers with tank cleaning
$tank_cleaning = []; $tank_cleaning = [];
/** @var int $customer_number */ /** @var int $customer_number */
foreach ( $customer_numbers as $customer_number ) { foreach ( $customer_numbers as $customer_number ) {
$tmp_tank_cleaning = [ $tank_cleaning[] = self::getCustomerFromList(
'customer_number' => $customer_number, (int)$customer_number,
'customer_name' => (new \objects\users_o())->getCustomerName($customer_number), $customersWithTransactions
'transactions' => [], );
'requires_action' => false,
];
// Check if the customer has a tank cleaning order within the date range
$orders = (new \objects\orders_o())->getTankCleaningTransactions($customer_number, false, $dateFrom, $dateTo);
/** @var orders_o $order */
foreach ( $orders as $order ) {
// Add the order to the tank cleaning transactions
$tmp_tank_cleaning['transactions'][] = [
'id' => $order->id,
];
}
// If there are transactions, set requires_action to false
if (count($tmp_tank_cleaning['transactions']) > 0) {
// Check if there's any transaction that has not been booked yet
$requires_action = false;
foreach ( $tmp_tank_cleaning['transactions'] as $transaction ) {
$order = (new orders_o())->select($transaction['id']);
if (!$order->isBooked()) {
$requires_action = true;
break;
}
}
$tmp_tank_cleaning['requires_action'] = $requires_action;
}
// Add the tank cleaning to the list if it has transactions // Add the tank cleaning to the list if it has transactions
$tank_cleaning[] = $tmp_tank_cleaning;
} }
return $tank_cleaning; return $tank_cleaning;
} }
/** /**
* @throws Exception * Filters the customer numbers based on whether they have transactions in the specified date range.
*
* @param array $customer_numbers The customer numbers to filter.
* @param array $customersWithTransactions The customers with transactions in the specified date range.
*/ */
private static function getCustomersWithTransactions(string $dateFrom, string $dateTo): array private static function filterCustomersWithTransactions(array &$customer_numbers, array $customersWithTransactions): void
{ {
// Define the customers with orders in the specified date range // Filter out customers that do not have any transactions in the specified date range
$customers = (new orders_o)->getCustomersWithOrdersInDateRange($dateFrom, $dateTo); $customer_numbers = array_filter($customer_numbers, function ($customer_number) use ($customersWithTransactions) {
$customer_numbers_processed = []; // Check if the customer has any transactions in the specified date range
$tmp = []; foreach ( $customersWithTransactions as $customer ) {
foreach ( $customers as $customer ) { if ($customer['customer_number'] === $customer_number) {
if (in_array((int)$customer->customer_number->value(), $customer_numbers_processed)) { return true;
// Skip if the customer has already been processed }
continue;
} }
$customer_numbers_processed[] = (int)$customer->customer_number->value(); return false;
// Construct the customer object with transactions });
$tmp[] = self::constructCustomerObject(
(int)$customer->customer_number->value(),
(new \objects\users_o())->getCustomerName((int)$customer->customer_number->value()),
(new \objects\orders_o())->getTransactionsForCustomer(
(int)$customer->customer_number->value(),
$dateFrom,
$dateTo
),
);
}
return $tmp;
} }
/** /**
* @param int $customer_number
* @param string $customer_name
* @param orders_o[] $transactions
* @param bool $requires_action
* @return array
* @throws Exception * @throws Exception
*/ */
protected static function constructCustomerObject( private static function getSpecialArrangements(string $dateFrom, string $dateTo, array $customersWithTransactions = null): array
int $customer_number,
string $customer_name,
array $transactions = [],
bool $requires_action = false
): array
{ {
return [ // If customersWithTransactions is not provided, get all customers with transactions in the specified date range
'customer_number' => $customer_number, if ($customersWithTransactions === null) {
'customer_name' => $customer_name, $customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
'transactions' => $parsed_transactions = array_map(function ($transaction) { }
return [ // Get all customers with tank cleaning
'id' => $transaction->id, $customer_numbers = (new \objects\users_o())->getCustomersWithSpecialArrangements();
'date' => $transaction->created_at->value(), // Filter out customers that do not have any transactions in the specified date range
'amount' => $transaction->getNetAmount(), self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
'booked' => $transaction->isBooked(), $special_arrangements = [];
]; /** @var int $customer_number */
}, $transactions), foreach ( $customer_numbers as $customer_number ) {
'requires_action' => self::checkRequiresAction($parsed_transactions, $requires_action), $special_arrangements[] = self::constructCustomerObject(
]; (int)$customer_number,
(new \objects\users_o())->getCustomerName((int)$customer_number) ?? 'Unknown Customer',
(new orders_o())->getTransactionsForCustomer(
(int)$customer_number,
$dateFrom,
$dateTo,
),
false,
);
}
return $special_arrangements;
} }
private static function checkRequiresAction(array $parsed_transactions, bool $requires_action): bool /**
* @throws Exception
*/
private static function getInvoicingPerOrder(string $dateFrom, string $dateTo, array $customersWithTransactions = null): array
{ {
// If requires_action is already set to true, return true // Get all customers with the invoicing per order attribute
if ($requires_action) { $customer_numbers = (new users_o())->getCustomerNumbersWithAttributes(['invoiceAllOrdersIndividually']);
return true; // Filter out customers that do not have any transactions in the specified date range
if ($customersWithTransactions === null) {
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
} }
// Check if any transaction is not booked self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
foreach ( $parsed_transactions as $transaction ) {
if (!$transaction['booked']) { $invoicing_per_order = [];
return true; /** @var int $customer_number */
} foreach ( $customer_numbers as $customer_number ) {
// Add the invoicing per order to the list
$invoicing_per_order[] = self::getCustomerFromList((int)$customer_number, $customersWithTransactions);
} }
// If all transactions are booked, return false return $invoicing_per_order;
return false;
} }
} }
@@ -278,6 +278,8 @@ class vehiclesRoute
$vehicle->type->set( $vehicle->type->set(
(int)$type (int)$type
); );
// TODO: Make the potential vehicleTypeId reflect the correct type.
// It's currently possible to set the vehicleTypeId to a type that is not allowed for the vehicle.
} }
} }
if (self::isParametersSet(['reg'])) { if (self::isParametersSet(['reg'])) {
@@ -314,6 +316,7 @@ class vehiclesRoute
$vehicle->reference->set($reference); $vehicle->reference->set($reference);
} }
} }
$vehicle->objectChanged();
// Return the vehicle // Return the vehicle
$response->success( $response->success(
$vehicle->asArray() $vehicle->asArray()