Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
feb95a5375 | ||
|
|
7ac90f70c9 |
@@ -906,6 +906,68 @@ class orders_o extends db
|
||||
return (bool)$count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp of the most recent completed wash for a license plate.
|
||||
* Used by the front page to show the "last washed" hint when a plate is
|
||||
* scanned (DHL trailer pick-up use case, TRU-78 / DRIFT 17).
|
||||
*
|
||||
* Only orders that have at least one non-deleted order item are
|
||||
* considered (mirrors the contract used by customer_vehicles_o::
|
||||
* getLastOrderByPlate() so the timestamp is always backed by a real wash).
|
||||
*
|
||||
* @param string $reg_1 The license plate to look up
|
||||
* @return string|null MySQL datetime string of the most recent qualifying
|
||||
* order's `created_at`, or null when the plate has
|
||||
* never been washed.
|
||||
*/
|
||||
public function getLastWashTimestampForPlate(string $reg_1): ?string
|
||||
{
|
||||
$normalized_reg_1 = trim($reg_1);
|
||||
if ($normalized_reg_1 === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$orders = self::getFieldsWhere([
|
||||
'reg_1' => $normalized_reg_1,
|
||||
'deleted_at' => null,
|
||||
], [
|
||||
'id',
|
||||
]);
|
||||
|
||||
// Walk the orders newest-first and return the first one that actually
|
||||
// has at least one non-deleted order item.
|
||||
$candidate_ids = array_reverse(array_map(static function ($row) {
|
||||
return (int)($row['id'] ?? 0);
|
||||
}, $orders));
|
||||
|
||||
foreach ($candidate_ids as $order_id) {
|
||||
if ($order_id <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$has_items = (new order_items_o())->getFieldsWhere([
|
||||
'order_id' => $order_id,
|
||||
'deleted_at' => null,
|
||||
], ['id']);
|
||||
|
||||
if (count($has_items) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$details = self::getFieldsWhere([
|
||||
'id' => $order_id,
|
||||
'deleted_at' => null,
|
||||
], ['created_at']);
|
||||
|
||||
$created_at = $details[0]['created_at'] ?? null;
|
||||
if (is_string($created_at) && $created_at !== '') {
|
||||
return $created_at;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFixedPricingTransactions(int $customer_number, false $asArray, string|null $dateFrom = null, string|null $dateTo = null): array
|
||||
{
|
||||
// Get the fixed pricing transactions for a customer
|
||||
|
||||
@@ -10344,8 +10344,11 @@ paths:
|
||||
post:
|
||||
tags:
|
||||
- Modules
|
||||
summary: Create Stripe invoice
|
||||
description: Create an invoice in Stripe
|
||||
summary: Create Stripe invoice (retired - TRU-74 / DRIFT 13)
|
||||
description: |
|
||||
Retired in favour of in-store card payments. Always returns HTTP 410
|
||||
with `code: stripe_email_payment_disabled` so the POS can fall back
|
||||
to the standard card-payment flow.
|
||||
operationId: createStripeInvoice
|
||||
requestBody:
|
||||
required: false
|
||||
@@ -10353,11 +10356,41 @@ paths:
|
||||
application/json:
|
||||
schema: {}
|
||||
responses:
|
||||
'201':
|
||||
description: Stripe invoice created successfully
|
||||
'410':
|
||||
description: Direct Stripe payment links by email are no longer available
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: stripe_email_payment_disabled
|
||||
message:
|
||||
type: string
|
||||
delete:
|
||||
tags:
|
||||
- Modules
|
||||
summary: Cancel/clean up a legacy Stripe hosted invoice
|
||||
description: |
|
||||
Void a pre-existing Stripe hosted invoice that was created before
|
||||
direct payment links were retired from POS (TRU-74 / DRIFT 13).
|
||||
Card payments created via the new flow are not affected and use
|
||||
the standard payment-intent lifecycle instead.
|
||||
operationId: cancelLegacyStripeInvoice
|
||||
parameters:
|
||||
- name: order_id
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Legacy Stripe hosted invoice was voided
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
|
||||
/modules/stripe/terminal/readers:
|
||||
get:
|
||||
|
||||
@@ -120,11 +120,21 @@ class plateScansRoute
|
||||
'type' => (int)$tmp_scan_vehicle['type'],
|
||||
];
|
||||
}
|
||||
// TRU-78 / DRIFT 17: enrich each scan with the
|
||||
// timestamp of the most recent completed wash for
|
||||
// that plate so the POS landing page can show
|
||||
// "last washed" at a glance when DHL trailers are
|
||||
// being picked up.
|
||||
$plate_value = (string)$scan['plate'];
|
||||
$tmp_scan_last_wash = [
|
||||
'last_wash' => (new orders_o())->getLastWashTimestampForPlate($plate_value),
|
||||
];
|
||||
// Return the object as an array
|
||||
return [
|
||||
...$scan,
|
||||
...$tmp_scan_customer,
|
||||
...$tmp_scan_seen_before,
|
||||
...$tmp_scan_last_wash,
|
||||
];
|
||||
},
|
||||
$number_plate_scans->forceRestrictFilters(
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
it('orders_o exposes getLastWashTimestampForPlate that filters out orders without order items', function (): void {
|
||||
$ordersFile = app_path('objects/orders_o.php');
|
||||
expect(is_file($ordersFile))->toBeTrue();
|
||||
|
||||
$ordersCode = preg_replace('/\s+/', ' ', (string)file_get_contents($ordersFile));
|
||||
|
||||
expect($ordersCode)->toContain('public function getLastWashTimestampForPlate(string $reg_1): ?string');
|
||||
|
||||
// The helper must look at non-deleted orders with non-deleted order items,
|
||||
// matching the contract used by customer_vehicles_o::getLastOrderByPlate().
|
||||
expect($ordersCode)->toContain("'reg_1' => $normalized_reg_1");
|
||||
expect($ordersCode)->toContain("'deleted_at' => null");
|
||||
expect($ordersCode)->toContain("'order_id' => $order_id");
|
||||
expect($ordersCode)->toContain('return $created_at;');
|
||||
expect($ordersCode)->toContain('return null;');
|
||||
});
|
||||
|
||||
it('plateScansRoute enriches GET /numberplatescans with last_wash per scan (TRU-78)', function (): void {
|
||||
$routeFile = app_path('routes/plateScansRoute.php');
|
||||
$ordersFile = app_path('objects/orders_o.php');
|
||||
|
||||
expect(is_file($routeFile))->toBeTrue();
|
||||
expect(is_file($ordersFile))->toBeTrue();
|
||||
|
||||
$routeCode = preg_replace('/\s+/', ' ', (string)file_get_contents($routeFile));
|
||||
$ordersCode = preg_replace('/\s+/', ' ', (string)file_get_contents($ordersFile));
|
||||
|
||||
// The route already loads orders_o and customer_vehicles_o; verify the
|
||||
// new enrichment is wired in the GET /numberplatescans handler.
|
||||
expect($routeCode)->toContain("\$this->get('/numberplatescans', function () {");
|
||||
expect($routeCode)->toContain("'last_wash'");
|
||||
expect($routeCode)->toContain('getLastWashTimestampForPlate');
|
||||
expect($routeCode)->toContain("'last_wash' => (new orders_o())->getLastWashTimestampForPlate");
|
||||
expect($routeCode)->toContain('$tmp_scan_last_wash');
|
||||
|
||||
// The helper definition must live in orders_o so the enrichment is real,
|
||||
// not a stub.
|
||||
expect($ordersCode)->toContain('public function getLastWashTimestampForPlate(string $reg_1): ?string');
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
it('documents the retired Stripe hosted invoice creation route in the public OpenAPI spec (TRU-74)', function (): void {
|
||||
$openApiFile = dirname(__DIR__, 3) . '/openapi.yaml';
|
||||
$contents = file_get_contents($openApiFile);
|
||||
|
||||
expect($contents)->not->toBeFalse();
|
||||
|
||||
$needle = " /modules/stripe/invoice:";
|
||||
$start = strpos($contents, $needle);
|
||||
expect($start)->not->toBeFalse();
|
||||
|
||||
$nextPathStart = strpos($contents, "\n /", $start + strlen($needle));
|
||||
if ($nextPathStart === false) {
|
||||
$nextPathStart = strlen($contents);
|
||||
}
|
||||
|
||||
$block = substr($contents, $start, $nextPathStart - $start);
|
||||
// Normalise trailing whitespace so the assertion is stable across editors.
|
||||
$normalised = preg_replace('/[ \t]+$/m', '', $block);
|
||||
|
||||
expect($normalised)->toContain(" /modules/stripe/invoice:");
|
||||
expect($normalised)->toContain('summary: Create Stripe invoice (retired - TRU-74 / DRIFT 13)');
|
||||
expect($normalised)->toContain("'410':");
|
||||
expect($normalised)->toContain('stripe_email_payment_disabled');
|
||||
expect($normalised)->toContain('Cancel/clean up a legacy Stripe hosted invoice');
|
||||
expect($normalised)->toContain('cancelLegacyStripeInvoice');
|
||||
});
|
||||
Reference in New Issue
Block a user