|
|
|
@@ -7,6 +7,7 @@ use Exception;
|
|
|
|
|
use objects\customer_vehicles_o;
|
|
|
|
|
use objects\logs_o;
|
|
|
|
|
use objects\orders_o;
|
|
|
|
|
use objects\users_o;
|
|
|
|
|
use traits\route_t;
|
|
|
|
|
|
|
|
|
|
class InvoicingPeriodRoute
|
|
|
|
@@ -33,6 +34,8 @@ class InvoicingPeriodRoute
|
|
|
|
|
// Require the dateFrom and dateTo parameters to be valid dates
|
|
|
|
|
self::requireDateFormat($dateFrom, '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
|
|
|
|
|
$response->success([...self::getInvoicingPeriod($dateFrom, $dateTo)]);
|
|
|
|
|
// Log the incident
|
|
|
|
@@ -68,14 +71,17 @@ class InvoicingPeriodRoute
|
|
|
|
|
*/
|
|
|
|
|
private static function getInvoicingPeriod(string $dateFrom, string $dateTo): array
|
|
|
|
|
{
|
|
|
|
|
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
|
|
|
|
|
return [
|
|
|
|
|
'dateFrom' => $dateFrom,
|
|
|
|
|
'dateTo' => $dateTo,
|
|
|
|
|
'types' => [
|
|
|
|
|
'vehicle_subscriptions' => self::getVehicleSubscriptions($dateFrom, $dateTo),
|
|
|
|
|
'fixed_pricing' => self::getFixedPricing($dateFrom, $dateTo),
|
|
|
|
|
'tank_cleaning' => self::getTankCleaning($dateFrom, $dateTo),
|
|
|
|
|
'all' => self::getCustomersWithTransactions($dateFrom, $dateTo),
|
|
|
|
|
'vehicle_subscriptions' => self::getVehicleSubscriptions($dateFrom, $dateTo, $customersWithTransactions),
|
|
|
|
|
'fixed_pricing' => self::getFixedPricing($dateFrom, $dateTo, $customersWithTransactions),
|
|
|
|
|
'tank_cleaning' => self::getTankCleaning($dateFrom, $dateTo, $customersWithTransactions),
|
|
|
|
|
'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
|
|
|
|
|
*/
|
|
|
|
|
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();
|
|
|
|
|
// Get all customers with vehicle subscriptions
|
|
|
|
|
$subscriptions = [];
|
|
|
|
|
/** @var int $customer_number */
|
|
|
|
|
foreach ( $customer_numbers as $customer_number ) {
|
|
|
|
|
$tmp_subscription = [
|
|
|
|
|
'customer_number' => $customer_number,
|
|
|
|
|
'customer_name' => (new \objects\users_o())->getCustomerName($customer_number),
|
|
|
|
|
'transactions' => [],
|
|
|
|
|
'requires_action' => 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;
|
|
|
|
|
$subscriptions[] = (self::getCustomerFromList((int)$customer_number, $customersWithTransactions)) ?? self::constructCustomerObject(
|
|
|
|
|
(int)$customer_number,
|
|
|
|
|
(new \objects\users_o())->getCustomerName((int)$customer_number) ?? 'Unknown Customer',
|
|
|
|
|
[],
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
@@ -137,7 +229,7 @@ class InvoicingPeriodRoute
|
|
|
|
|
foreach ( $customer_numbers as $customer_number ) {
|
|
|
|
|
$tmp_fixed_pricing = [
|
|
|
|
|
'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' => [],
|
|
|
|
|
'requires_action' => true,
|
|
|
|
|
];
|
|
|
|
@@ -173,112 +265,97 @@ class InvoicingPeriodRoute
|
|
|
|
|
/**
|
|
|
|
|
* @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();
|
|
|
|
|
self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
|
|
|
|
|
// Get all customers with tank cleaning
|
|
|
|
|
$tank_cleaning = [];
|
|
|
|
|
/** @var int $customer_number */
|
|
|
|
|
foreach ( $customer_numbers as $customer_number ) {
|
|
|
|
|
$tmp_tank_cleaning = [
|
|
|
|
|
'customer_number' => $customer_number,
|
|
|
|
|
'customer_name' => (new \objects\users_o())->getCustomerName($customer_number),
|
|
|
|
|
'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;
|
|
|
|
|
}
|
|
|
|
|
$tank_cleaning[] = self::getCustomerFromList(
|
|
|
|
|
(int)$customer_number,
|
|
|
|
|
$customersWithTransactions
|
|
|
|
|
);
|
|
|
|
|
// Add the tank cleaning to the list if it has transactions
|
|
|
|
|
$tank_cleaning[] = $tmp_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
|
|
|
|
|
$customers = (new orders_o)->getCustomersWithOrdersInDateRange($dateFrom, $dateTo);
|
|
|
|
|
$tmp = [];
|
|
|
|
|
foreach ( $customers as $customer ) {
|
|
|
|
|
$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;
|
|
|
|
|
// Filter out customers that do not have any transactions in the specified date range
|
|
|
|
|
$customer_numbers = array_filter($customer_numbers, function ($customer_number) use ($customersWithTransactions) {
|
|
|
|
|
// Check if the customer has any transactions in the specified date range
|
|
|
|
|
foreach ( $customersWithTransactions as $customer ) {
|
|
|
|
|
if ($customer['customer_number'] === $customer_number) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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
|
|
|
|
|
): array
|
|
|
|
|
private static function getSpecialArrangements(string $dateFrom, string $dateTo, array $customersWithTransactions = null): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'customer_number' => $customer_number,
|
|
|
|
|
'customer_name' => $customer_name,
|
|
|
|
|
'transactions' => $parsed_transactions = array_map(function ($transaction) {
|
|
|
|
|
return [
|
|
|
|
|
'id' => $transaction->id,
|
|
|
|
|
'date' => $transaction->created_at->value(),
|
|
|
|
|
'amount' => $transaction->getNetAmount(),
|
|
|
|
|
'booked' => $transaction->isBooked(),
|
|
|
|
|
];
|
|
|
|
|
}, $transactions),
|
|
|
|
|
'requires_action' => self::checkRequiresAction($parsed_transactions, $requires_action),
|
|
|
|
|
];
|
|
|
|
|
// If customersWithTransactions is not provided, get all customers with transactions in the specified date range
|
|
|
|
|
if ($customersWithTransactions === null) {
|
|
|
|
|
$customersWithTransactions = self::getCustomersWithTransactions($dateFrom, $dateTo);
|
|
|
|
|
}
|
|
|
|
|
// Get all customers with tank cleaning
|
|
|
|
|
$customer_numbers = (new \objects\users_o())->getCustomersWithSpecialArrangements();
|
|
|
|
|
// Filter out customers that do not have any transactions in the specified date range
|
|
|
|
|
self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
|
|
|
|
|
$special_arrangements = [];
|
|
|
|
|
/** @var int $customer_number */
|
|
|
|
|
foreach ( $customer_numbers as $customer_number ) {
|
|
|
|
|
$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
|
|
|
|
|
if ($requires_action) {
|
|
|
|
|
return true;
|
|
|
|
|
// Get all customers with the invoicing per order attribute
|
|
|
|
|
$customer_numbers = (new users_o())->getCustomerNumbersWithAttributes(['invoiceAllOrdersIndividually']);
|
|
|
|
|
// 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
|
|
|
|
|
foreach ( $parsed_transactions as $transaction ) {
|
|
|
|
|
if (!$transaction['booked']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
self::filterCustomersWithTransactions($customer_numbers, $customersWithTransactions);
|
|
|
|
|
|
|
|
|
|
$invoicing_per_order = [];
|
|
|
|
|
/** @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 false;
|
|
|
|
|
return $invoicing_per_order;
|
|
|
|
|
}
|
|
|
|
|
}
|