1742033bb7
## TRU-70: DRIFT 9 — Customer rule "auto-send invoice toggle (3rd
business day each month)"
Adds a per-customer rule that auto-sends the customer's invoice on the
3rd business day of each month. The rule is implemented as a new
customer attribute (`autoSendInvoiceThirdBusinessDay`) that can be
toggled through the existing `POST /customer/attributes` endpoint, plus
a daily cron task that, on the trigger day, enqueues every ready
collected invoice for export via the existing `economic_transfer_queue`.
## Linear
- **TRU-70** (DRIFT 9)
## Changes
- **Customer attribute (TRU-70 / DRIFT 9)**
- `classes/customer_rule_product_restriction_service.php`
Adds `'autoSendInvoiceThirdBusinessDay'` to `SUPPORTED_ATTRIBUTES` so
the existing customer-attributes route can persist the toggle.
- **3rd-business-day service**
- `classes/auto_send_invoice_third_business_day_service.php` (new)
- Computes the 3rd business day of any month (weekend-aware, holiday
provider override).
- `runOnce()` is a no-op on every day except the 3rd business day.
- On the trigger day, scans `customer_attributes` for opted-in customers
and loads their ready `collected_order_invoices` (not booked, not
closed, has at least one order, not deleted).
- Enqueues each via `economic_transfer_queue` and returns a summary
`{triggered, customers, collections_scanned, jobs_enqueued,
skipped_already_queued, errors[]}`.
- Public hooks (`loadEligibleCustomerNumbers`,
`loadReadyInvoiceCollections`) and protected `createTransferQueue()` are
designed for unit-test isolation so no live database is required.
- **Cron task (TRU-70 / DRIFT 9)**
- `modules/economic/cron/tasks.php`
Registers `'economic.auto_send_invoices_third_business_day'` with a 24h
interval, 15 min timeout, priority 25. Anchored in the economic module
because the actual export goes through `economic_transfer_queue`.
- `cron/Cron.php`
New `AutoSendInvoicesThirdBusinessDay()` handler. Mirrors the
surrounding cron-task conventions (`warn` + `error_log` breadcrumb on
failure) and only logs a one-liner when the trigger fires.
## Tests
- `tests/Unit/Cron/AutoSendInvoiceThirdBusinessDayTest.php` (new, 16
tests)
- The 3rd-business-day computation (weekday-start, weekend-start,
Saturday, 4th-business-day, holiday skip, holiday forward-shift).
- `thirdBusinessDayOfMonth()` helper for the three reference months used
in the spec (Aug/Sep 2026 and Jul 2026).
- `runOnce()` no-op path on non-trigger days.
- `runOnce()` summary on trigger day with zero opted-in customers.
- `runOnce()` summary with customers and ready collections.
- `runOnce()` per-collection error recording when enqueue throws.
- `clearOverrides()` reset.
- The new attribute is in
`customer_rule_product_restriction_service::SUPPORTED_ATTRIBUTES` and
exposes a stable `ATTRIBUTE` constant.
- `tests/Unit/Cron/CronTaskRegistryTest.php`
Updated the discovery assertion from 22 to 23 definitions and added
coverage that the new `'economic.auto_send_invoices_third_business_day'`
task is discovered with the expected schedule and module.
## Backwards compatibility
- The new attribute is additive; existing `customer_attributes` rows are
unaffected. The default customer has no auto-send rule.
- The cron task is registered in the standard task registry; the
existing `cron_worker` (15s poll) handles the trigger without any new
infrastructure.
- The service gracefully no-ops when the transfer queue is unavailable
in unit-test contexts; the production cron task will surface a `warn()`
breadcrumb if e-conomic is unreachable, exactly like the other economic
cron tasks.
## Verification
```
vendor/bin/pest tests/Unit/Cron/ tests/Unit/Customers/
# 43 passed (199 assertions)
```
---------
Co-authored-by: openclaw bugfix <openclaw@copenhagentruckwash.local>
Co-authored-by: bugfix <bugfix@truckwash.local>
Co-authored-by: bugfix-subagent <[email protected]>
77 lines
3.2 KiB
PHP
77 lines
3.2 KiB
PHP
<?php
|
|
|
|
return [
|
|
[
|
|
'id' => 'economic.sync_user_customer_discounts',
|
|
'legacy_name' => 'SyncUserEconomicCustomerDiscounts',
|
|
'name' => 'Sync e-conomic customer discounts',
|
|
'description' => 'Clears cached customer discounts so e-conomic discount data can refresh.',
|
|
'module' => 'economic',
|
|
'handler' => 'SyncUserEconomicCustomerDiscounts',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 180],
|
|
'timeout_seconds' => 300,
|
|
'estimated_duration_ms' => 2000,
|
|
'priority' => 30,
|
|
],
|
|
[
|
|
'id' => 'economic.sync_user_customer_details',
|
|
'legacy_name' => 'SyncUserEconomicCustomerDetails',
|
|
'name' => 'Sync e-conomic customer details',
|
|
'description' => 'Refreshes customer details and search documents from e-conomic.',
|
|
'module' => 'economic',
|
|
'handler' => 'SyncUserEconomicCustomerDetails',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 43200],
|
|
'timeout_seconds' => 1800,
|
|
'estimated_duration_ms' => 30000,
|
|
'priority' => 110,
|
|
],
|
|
[
|
|
'id' => 'economic.sync_system_search_customer_index',
|
|
'legacy_name' => 'SyncSystemSearchEconomicCustomerIndex',
|
|
'name' => 'Sync e-conomic customer search index',
|
|
'description' => 'Refreshes the system search e-conomic customer index.',
|
|
'module' => 'economic',
|
|
'handler' => 'SyncSystemSearchEconomicCustomerIndex',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 900],
|
|
'timeout_seconds' => 900,
|
|
'estimated_duration_ms' => 10000,
|
|
'priority' => 75,
|
|
],
|
|
[
|
|
'id' => 'economic.sync_invoice_status',
|
|
'legacy_name' => 'SyncEconomicInvoiceStatus',
|
|
'name' => 'Sync e-conomic invoice status',
|
|
'description' => 'Checks e-conomic invoice errors and draft status.',
|
|
'module' => 'economic',
|
|
'handler' => 'SyncEconomicInvoiceStatus',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 120],
|
|
'timeout_seconds' => 300,
|
|
'estimated_duration_ms' => 3000,
|
|
'priority' => 35,
|
|
],
|
|
[
|
|
'id' => 'economic.transfer_queue',
|
|
'legacy_name' => 'EconomicTransferQueueCron',
|
|
'name' => 'Process e-conomic transfer queue',
|
|
'description' => 'Processes pending e-conomic transfer queue jobs in bounded batches.',
|
|
'module' => 'economic',
|
|
'handler' => 'EconomicTransferQueueCron',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 30],
|
|
'timeout_seconds' => 300,
|
|
'estimated_duration_ms' => 3000,
|
|
'priority' => 20,
|
|
],
|
|
[
|
|
'id' => 'economic.auto_send_invoices_third_business_day',
|
|
'legacy_name' => 'AutoSendInvoicesThirdBusinessDay',
|
|
'name' => 'Auto-send invoices on the 3rd business day each month',
|
|
'description' => 'For customers with the autoSendInvoiceThirdBusinessDay attribute, enqueue every ready collected invoice for export on the 3rd business day of the month. The handler is a no-op on every other day.',
|
|
'module' => 'economic',
|
|
'handler' => 'AutoSendInvoicesThirdBusinessDay',
|
|
'schedule' => ['type' => 'interval', 'seconds' => 86400],
|
|
'timeout_seconds' => 900,
|
|
'estimated_duration_ms' => 5000,
|
|
'priority' => 25,
|
|
],
|
|
];
|