Merge pull request #292 from copenhagentruckwash/fix/limited-backoffice-price-save
[codex] Fix limited backoffice price saves on legacy schema
This commit is contained in:
@@ -291,10 +291,15 @@ class limited_backoffice_service
|
||||
$mysqli->begin_transaction();
|
||||
|
||||
try {
|
||||
$priceUpdateAssignments = ['`price` = VALUES(`price`)'];
|
||||
if ($this->tableHasColumn('product_department_prices', 'updated_at')) {
|
||||
$priceUpdateAssignments[] = '`updated_at` = CURRENT_TIMESTAMP';
|
||||
}
|
||||
|
||||
$statement = $mysqli->prepare(
|
||||
'INSERT INTO `product_department_prices` (`department_id`, `product_id`, `price`)
|
||||
VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE `price` = VALUES(`price`), `updated_at` = CURRENT_TIMESTAMP'
|
||||
ON DUPLICATE KEY UPDATE ' . implode(', ', $priceUpdateAssignments)
|
||||
);
|
||||
if ($statement === false) {
|
||||
throw new \RuntimeException('Unable to prepare department price update.');
|
||||
|
||||
@@ -85,6 +85,32 @@ function limited_backoffice_without_users_deleted_at(callable $callback): void
|
||||
}
|
||||
}
|
||||
|
||||
function limited_backoffice_without_department_prices_updated_at(callable $callback): void
|
||||
{
|
||||
$db = api_test_runtime()->db();
|
||||
$column = $db->query("SHOW COLUMNS FROM `product_department_prices` LIKE 'updated_at'");
|
||||
if ($column === false) {
|
||||
throw new RuntimeException('Unable to inspect product_department_prices.updated_at test column.');
|
||||
}
|
||||
|
||||
$hadColumn = (int)$column->num_rows > 0;
|
||||
if ($hadColumn) {
|
||||
$db->query('ALTER TABLE `product_department_prices` DROP COLUMN `updated_at`');
|
||||
}
|
||||
|
||||
try {
|
||||
$callback();
|
||||
} finally {
|
||||
if ($hadColumn) {
|
||||
$db->query(
|
||||
'ALTER TABLE `product_department_prices`
|
||||
ADD COLUMN `updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
AFTER `created_at`'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it('lists and updates explicit prices only for assigned departments', function (): void {
|
||||
api_test_covers('GET /limited-backoffice/departments', 'happy');
|
||||
api_test_covers('GET /limited-backoffice/departments/{departmentId}/prices', 'happy');
|
||||
@@ -148,6 +174,43 @@ it('lists and updates explicit prices only for assigned departments', function (
|
||||
expect(limited_backoffice_price_value((int)$otherDepartment['id'], (int)$product['id']))->toBe(4321);
|
||||
});
|
||||
|
||||
it('updates department prices when the price table has no updated_at column', function (): void {
|
||||
api_test_covers('PUT /limited-backoffice/departments/{departmentId}/prices', 'schema compatibility');
|
||||
|
||||
$department = api_fixtures()->createDepartment(['name' => 'Limited Prices Legacy Schema']);
|
||||
$category = api_fixtures()->createCategory(['name' => 'Limited Prices Legacy Category']);
|
||||
$products = [
|
||||
api_fixtures()->createProduct(['name' => 'Legacy Price One', 'category' => $category['id']]),
|
||||
api_fixtures()->createProduct(['name' => 'Legacy Price Two', 'category' => $category['id']]),
|
||||
api_fixtures()->createProduct(['name' => 'Legacy Price Three', 'category' => $category['id']]),
|
||||
];
|
||||
api_fixtures()->linkDepartmentCategory((int)$department['id'], (int)$category['id']);
|
||||
|
||||
foreach ($products as $index => $product) {
|
||||
limited_backoffice_price_insert((int)$department['id'], (int)$product['id'], 100 + $index);
|
||||
}
|
||||
|
||||
$session = limited_backoffice_manager_session([(int)$department['id']]);
|
||||
limited_backoffice_without_department_prices_updated_at(function () use ($department, $products, $session): void {
|
||||
$updated = api_client()->put('/limited-backoffice/departments/' . (int)$department['id'] . '/prices', [
|
||||
'prices' => [
|
||||
['product_id' => (int)$products[0]['id'], 'price' => '999999'],
|
||||
['product_id' => (int)$products[1]['id'], 'price' => '999999'],
|
||||
['product_id' => (int)$products[2]['id'], 'price' => '99999'],
|
||||
],
|
||||
], $session['headers']);
|
||||
|
||||
$updated
|
||||
->assertStatus(200)
|
||||
->assertEnvelope()
|
||||
->assertSuccess();
|
||||
|
||||
expect(limited_backoffice_price_value((int)$department['id'], (int)$products[0]['id']))->toBe(999999);
|
||||
expect(limited_backoffice_price_value((int)$department['id'], (int)$products[1]['id']))->toBe(999999);
|
||||
expect(limited_backoffice_price_value((int)$department['id'], (int)$products[2]['id']))->toBe(99999);
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects cross-department price access, body spoofing, and outside products', function (): void {
|
||||
api_test_covers('GET /limited-backoffice/departments/{departmentId}/prices', 'auth');
|
||||
api_test_covers('PUT /limited-backoffice/departments/{departmentId}/prices', 'auth');
|
||||
|
||||
Reference in New Issue
Block a user