Add customer rule cleanup for spotfree addon products with preview and apply functionality
This commit is contained in:
@@ -12,6 +12,7 @@ class customer_product_rule_service
|
||||
|
||||
private const ADDON_CATEGORY_ID = 4;
|
||||
private const TANK_CLEANING_CATEGORY_ID = 5;
|
||||
private const SPOT_FREE_PRODUCT_IDS = [23, 24];
|
||||
|
||||
/**
|
||||
* @return array{rule:string,message:string}|null
|
||||
@@ -52,7 +53,7 @@ class customer_product_rule_service
|
||||
}
|
||||
|
||||
if ($customer->doesUserHaveAttribute('restrictSpotFree')
|
||||
&& $this->containsAny($searchableProduct, ['spot free', 'spotfree'])) {
|
||||
&& $this->isSpotFreeProduct((int)$product->id, $searchableProduct)) {
|
||||
return $this->violation('restrictSpotFree');
|
||||
}
|
||||
|
||||
@@ -101,6 +102,15 @@ class customer_product_rule_service
|
||||
return $this->containsAny($searchableProduct, ['tank cleaning', 'tankcleaning', 'tankrens', 'tank rens']);
|
||||
}
|
||||
|
||||
private function isSpotFreeProduct(int $productId, string $searchableProduct): bool
|
||||
{
|
||||
if (in_array($productId, self::SPOT_FREE_PRODUCT_IDS, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->containsAny($searchableProduct, ['spot free', 'spotfree', 'skylning med ro']);
|
||||
}
|
||||
|
||||
private function searchableProductText(products_o $product, string $categoryName): string
|
||||
{
|
||||
return strtolower(trim((string)$product->name->value() . ' ' . $categoryName));
|
||||
|
||||
@@ -22,6 +22,7 @@ class invoice_period_flag_service
|
||||
private const ORDER_FIELDS = ['customer', 'reference', 'po', 'notes'];
|
||||
private const ORDER_ITEM_FIELDS = ['notes', 'quantity', 'reference', 'price'];
|
||||
private const WASH_CERTIFICATE_PRODUCT_ID = 41;
|
||||
private const SPOT_FREE_PRODUCT_IDS = [23, 24];
|
||||
private array $economicCustomerDiscountCache = [];
|
||||
private array $userDisplayNameCache = [];
|
||||
private array $orderItemsPreviewCache = [];
|
||||
@@ -939,7 +940,7 @@ class invoice_period_flag_service
|
||||
}
|
||||
|
||||
$restrictedProducts = [
|
||||
'restrictSpotFree' => ['customer_rule_restrict_spot_free', ['spot free', 'spotfree']],
|
||||
'restrictSpotFree' => ['customer_rule_restrict_spot_free', ['spot free', 'spotfree', 'skylning med ro']],
|
||||
'restrictInteriorCleaning' => ['customer_rule_restrict_interior_cleaning', ['interior', 'indvendig']],
|
||||
'exemptFromAdministrationFee' => ['customer_rule_exempt_from_administration_fees', ['administration fee', 'administrationsgebyr', 'administration']],
|
||||
];
|
||||
@@ -2097,6 +2098,11 @@ class invoice_period_flag_service
|
||||
|
||||
private function rowMatchesProductTerms(array $row, array $terms): bool
|
||||
{
|
||||
if (in_array((int)($row['product_id'] ?? 0), self::SPOT_FREE_PRODUCT_IDS, true)
|
||||
&& in_array('spotfree', array_map('strtolower', $terms), true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$haystack = strtolower(trim(
|
||||
(string)($row['product_name'] ?? '') . ' ' .
|
||||
(string)($row['category_name'] ?? '')
|
||||
|
||||
@@ -93,6 +93,84 @@ it('previews and applies customer rule cleanup only after exact typed confirmati
|
||||
->and(bulk_action_order_item_deleted_at((int)$orderItem['id']))->not->toBeNull();
|
||||
});
|
||||
|
||||
it('previews and applies customer rule cleanup for both spotfree addon products', function (): void {
|
||||
api_test_covers('POST /collected-invoices/bulk-actions/preview', 'customer-rule-cleanup-spotfree-addons');
|
||||
api_test_covers('POST /collected-invoices/bulk-actions/apply', 'customer-rule-cleanup-spotfree-addons');
|
||||
|
||||
$customer = api_fixtures()->createUser(['display_name' => 'Bulk Cleanup Spotfree Addons Customer']);
|
||||
api_fixtures()->addCustomerAttribute((int)$customer['id'], 'restrictSpotFree');
|
||||
$department = api_fixtures()->createDepartment();
|
||||
$invoiceCollection = api_fixtures()->createInvoiceCollection([
|
||||
'customer_number' => $customer['customer_number'],
|
||||
]);
|
||||
$order = api_fixtures()->createOrder([
|
||||
'customer_id' => $customer['customer_number'],
|
||||
'department_id' => $department['id'],
|
||||
'invoice_collection_id' => $invoiceCollection['id'],
|
||||
]);
|
||||
$spotfreeVanProduct = api_fixtures()->createProduct([
|
||||
'id' => 23,
|
||||
'name' => 'Skylning med RO - Varevogn',
|
||||
'category' => 4,
|
||||
'price' => 39,
|
||||
]);
|
||||
$spotfreeTruckProduct = api_fixtures()->createProduct([
|
||||
'id' => 24,
|
||||
'name' => 'Skylning med RO - Lastbil',
|
||||
'category' => 4,
|
||||
'price' => 39,
|
||||
]);
|
||||
$vanOrderItem = api_fixtures()->createOrderItem([
|
||||
'order_id' => $order['id'],
|
||||
'product_id' => $spotfreeVanProduct['id'],
|
||||
'cashier_id' => 1,
|
||||
'price' => 39,
|
||||
]);
|
||||
$truckOrderItem = api_fixtures()->createOrderItem([
|
||||
'order_id' => $order['id'],
|
||||
'product_id' => $spotfreeTruckProduct['id'],
|
||||
'cashier_id' => 1,
|
||||
'price' => 39,
|
||||
]);
|
||||
$session = api_fixtures()->createUserSession(['reset_collected_invoice_economic']);
|
||||
|
||||
$previewResponse = api_client()->post('/collected-invoices/bulk-actions/preview', [
|
||||
'action' => 'remove_customer_rule_violations',
|
||||
'invoice_collection_ids' => [$invoiceCollection['id']],
|
||||
'locale' => 'en',
|
||||
], $session['headers']);
|
||||
|
||||
$previewResponse
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
|
||||
$preview = $previewResponse->data();
|
||||
$previewOrderItemIds = array_map('intval', array_column($preview['order_items'] ?? [], 'order_item_id'));
|
||||
sort($previewOrderItemIds);
|
||||
|
||||
expect($preview['summary']['changed_count'] ?? null)->toBe(2)
|
||||
->and($previewOrderItemIds)->toBe([(int)$vanOrderItem['id'], (int)$truckOrderItem['id']])
|
||||
->and(bulk_action_order_item_deleted_at((int)$vanOrderItem['id']))->toBeNull()
|
||||
->and(bulk_action_order_item_deleted_at((int)$truckOrderItem['id']))->toBeNull();
|
||||
|
||||
$applyResponse = api_client()->post('/collected-invoices/bulk-actions/apply', [
|
||||
'preview_id' => $preview['preview_id'],
|
||||
'action' => 'remove_customer_rule_violations',
|
||||
'invoice_collection_ids' => [$invoiceCollection['id']],
|
||||
'confirmation_text' => 'Confirm',
|
||||
'locale' => 'en',
|
||||
], $session['headers']);
|
||||
|
||||
$applyResponse
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
|
||||
expect(bulk_action_order_item_deleted_at((int)$vanOrderItem['id']))->not->toBeNull()
|
||||
->and(bulk_action_order_item_deleted_at((int)$truckOrderItem['id']))->not->toBeNull();
|
||||
});
|
||||
|
||||
it('merges selected invoice collections into the explicit target after confirmation', function (): void {
|
||||
api_test_covers('POST /collected-invoices/bulk-actions/preview', 'merge');
|
||||
api_test_covers('POST /collected-invoices/bulk-actions/apply', 'merge');
|
||||
|
||||
Reference in New Issue
Block a user