Compare commits
2 Commits
197df3bec4
...
adf13410ba
| Author | SHA1 | Date | |
|---|---|---|---|
| adf13410ba | |||
| d52a215916 |
@@ -133,7 +133,10 @@ class SyncOrderAction
|
||||
$warehouseId,
|
||||
$qty,
|
||||
"POS Order: " . $order->external_order_id,
|
||||
true
|
||||
true,
|
||||
null,
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
$order->id
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,10 @@ class SyncVendingOrderAction
|
||||
$warehouseId,
|
||||
$qty,
|
||||
"Vending Order: " . $order->external_order_id,
|
||||
true
|
||||
true,
|
||||
null,
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
$order->id
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ interface InventoryServiceInterface
|
||||
* @param string|null $slot
|
||||
* @return void
|
||||
*/
|
||||
public function decreaseStock(int $productId, int $warehouseId, float $quantity, ?string $reason = null, bool $force = false, ?string $slot = null): void;
|
||||
public function decreaseStock(int $productId, int $warehouseId, float $quantity, ?string $reason = null, bool $force = false, ?string $slot = null, ?string $referenceType = null, $referenceId = null): void;
|
||||
|
||||
/**
|
||||
* Get all active warehouses.
|
||||
|
||||
@@ -87,9 +87,9 @@ class InventoryService implements InventoryServiceInterface
|
||||
return $stock >= $quantity;
|
||||
}
|
||||
|
||||
public function decreaseStock(int $productId, int $warehouseId, float $quantity, ?string $reason = null, bool $force = false, ?string $slot = null): void
|
||||
public function decreaseStock(int $productId, int $warehouseId, float $quantity, ?string $reason = null, bool $force = false, ?string $slot = null, ?string $referenceType = null, $referenceId = null): void
|
||||
{
|
||||
DB::transaction(function () use ($productId, $warehouseId, $quantity, $reason, $force, $slot) {
|
||||
DB::transaction(function () use ($productId, $warehouseId, $quantity, $reason, $force, $slot, $referenceType, $referenceId) {
|
||||
$query = Inventory::where('product_id', $productId)
|
||||
->where('warehouse_id', $warehouseId)
|
||||
->where('quantity', '>', 0);
|
||||
@@ -108,7 +108,7 @@ class InventoryService implements InventoryServiceInterface
|
||||
if ($remainingToDecrease <= 0) break;
|
||||
|
||||
$decreaseAmount = min($inventory->quantity, $remainingToDecrease);
|
||||
$this->decreaseInventoryQuantity($inventory->id, $decreaseAmount, $reason);
|
||||
$this->decreaseInventoryQuantity($inventory->id, $decreaseAmount, $reason, $referenceType, $referenceId);
|
||||
$remainingToDecrease -= $decreaseAmount;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class InventoryService implements InventoryServiceInterface
|
||||
]);
|
||||
}
|
||||
|
||||
$this->decreaseInventoryQuantity($inventory->id, $remainingToDecrease, $reason);
|
||||
$this->decreaseInventoryQuantity($inventory->id, $remainingToDecrease, $reason, $referenceType, $referenceId);
|
||||
} else {
|
||||
throw new \Exception("庫存不足,無法扣除所有請求的數量。");
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ class TurnoverService
|
||||
->select('inventories.product_id', DB::raw('ABS(SUM(inventory_transactions.quantity)) as sales_qty_30d'))
|
||||
->join('inventories', 'inventory_transactions.inventory_id', '=', 'inventories.id')
|
||||
->where('inventory_transactions.type', '出庫') // Adjust type as needed based on actual data
|
||||
->where(function ($q) {
|
||||
$q->whereIn('inventory_transactions.reference_type', [
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
])->orWhereNull('inventory_transactions.reference_type');
|
||||
})
|
||||
->where('inventory_transactions.actual_time', '>=', $thirtyDaysAgo)
|
||||
->groupBy('inventories.product_id');
|
||||
|
||||
@@ -87,6 +93,12 @@ class TurnoverService
|
||||
->select('inventories.product_id', DB::raw('MAX(actual_time) as last_sale_date'))
|
||||
->join('inventories', 'inventory_transactions.inventory_id', '=', 'inventories.id')
|
||||
->where('inventory_transactions.type', '出庫')
|
||||
->where(function ($q) {
|
||||
$q->whereIn('inventory_transactions.reference_type', [
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
])->orWhereNull('inventory_transactions.reference_type');
|
||||
})
|
||||
->groupBy('inventories.product_id');
|
||||
|
||||
if ($warehouseId) {
|
||||
@@ -199,6 +211,12 @@ class TurnoverService
|
||||
// Get IDs of products sold in last 90 days
|
||||
$soldProductIds = InventoryTransaction::query()
|
||||
->where('type', '出庫')
|
||||
->where(function ($q) {
|
||||
$q->whereIn('reference_type', [
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
])->orWhereNull('reference_type');
|
||||
})
|
||||
->where('actual_time', '>=', $ninetyDaysAgo)
|
||||
->distinct()
|
||||
->pluck('inventory_id') // Wait, transaction links to inventory, inventory links to product.
|
||||
@@ -214,6 +232,12 @@ class TurnoverService
|
||||
$soldProductIdsQuery = DB::table('inventory_transactions')
|
||||
->join('inventories', 'inventory_transactions.inventory_id', '=', 'inventories.id')
|
||||
->where('inventory_transactions.type', '出庫')
|
||||
->where(function ($q) {
|
||||
$q->whereIn('inventory_transactions.reference_type', [
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
])->orWhereNull('inventory_transactions.reference_type');
|
||||
})
|
||||
->where('inventory_transactions.actual_time', '>=', $ninetyDaysAgo)
|
||||
->select('inventories.product_id')
|
||||
->distinct();
|
||||
@@ -236,6 +260,12 @@ class TurnoverService
|
||||
->join('inventories', 'inventory_transactions.inventory_id', '=', 'inventories.id')
|
||||
->join('products', 'inventories.product_id', '=', 'products.id')
|
||||
->where('inventory_transactions.type', '出庫')
|
||||
->where(function ($q) {
|
||||
$q->whereIn('inventory_transactions.reference_type', [
|
||||
\App\Modules\Integration\Models\SalesOrder::class,
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
])->orWhereNull('inventory_transactions.reference_type');
|
||||
})
|
||||
->where('inventory_transactions.actual_time', '>=', Carbon::now()->subDays($analysisDays))
|
||||
->when($warehouseId, fn($q) => $q->where('inventories.warehouse_id', $warehouseId))
|
||||
->when($categoryId, fn($q) => $q->where('products.category_id', $categoryId))
|
||||
|
||||
@@ -157,7 +157,9 @@ class SalesImportController extends Controller
|
||||
$deduction['quantity'],
|
||||
$reason,
|
||||
true, // Force deduction
|
||||
$deduction['slot'] // Location/Slot
|
||||
$deduction['slot'], // Location/Slot
|
||||
\App\Modules\Sales\Models\SalesImportBatch::class,
|
||||
$import->id
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ Star ERP 系統提供外部整合 API (Integration API) 供電商前台、POS
|
||||
|
||||
| 參數名稱 | 類型 | 必填 | 說明 |
|
||||
| :--- | :--- | :---: | :--- |
|
||||
| `warehouse_code` | String | **是** | 要查詢的倉庫代碼 (例如:`STORE-001`) |
|
||||
| `warehouse_code` | String | **是** | 要查詢的倉庫代碼 (例如:`STORE-001`,測試可使用預設建立之 `api-test-01`) |
|
||||
|
||||
### Response
|
||||
|
||||
@@ -94,7 +94,7 @@ Star ERP 系統提供外部整合 API (Integration API) 供電商前台、POS
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"warehouse_code": "STORE-001",
|
||||
"warehouse_code": "api-test-01",
|
||||
"data": [
|
||||
{
|
||||
"external_pos_id": "POS-ITEM-001",
|
||||
@@ -136,7 +136,7 @@ Star ERP 系統提供外部整合 API (Integration API) 供電商前台、POS
|
||||
| 欄位名稱 | 型態 | 必填 | 說明 |
|
||||
| :--- | :--- | :---: | :--- |
|
||||
| `external_order_id` | String | **是** | 第三方系統中的唯一訂單編號,不可重複 (Unique) |
|
||||
| `warehouse_code` | String | **是** | 指定扣除庫存的倉庫代碼 (例如:`STORE-001`)。若找不到對應倉庫將直接拒絕請求 |
|
||||
| `warehouse_code` | String | **是** | 指定扣除庫存的倉庫代碼 (例如:`api-test-01` 測試倉)。若找不到對應倉庫將直接拒絕請求 |
|
||||
| `payment_method` | String | 否 | 付款方式,僅接受:`cash`, `credit_card`, `line_pay`, `ecpay`, `transfer`, `other`。預設為 `cash` |
|
||||
| `sold_at` | String(Date) | 否 | 交易發生時間,預設為當下時間 (格式: YYYY-MM-DD HH:mm:ss) |
|
||||
| `items` | Array | **是** | 訂單明細陣列,至少需包含一筆商品 |
|
||||
@@ -153,7 +153,7 @@ Star ERP 系統提供外部整合 API (Integration API) 供電商前台、POS
|
||||
```json
|
||||
{
|
||||
"external_order_id": "ORD-20231026-0001",
|
||||
"warehouse_code": "STORE-001",
|
||||
"warehouse_code": "api-test-01",
|
||||
"payment_method": "credit_card",
|
||||
"sold_at": "2023-10-26 14:30:00",
|
||||
"items": [
|
||||
|
||||
@@ -175,6 +175,9 @@ class PosApiTest extends TestCase
|
||||
'Accept' => 'application/json',
|
||||
])->postJson('/api/v1/integration/orders', $payload);
|
||||
|
||||
$response->assertStatus(201)
|
||||
->assertJsonPath('message', 'Order synced and stock deducted successfully');
|
||||
|
||||
$response->assertStatus(201)
|
||||
->assertJsonPath('message', 'Order synced and stock deducted successfully');
|
||||
|
||||
@@ -196,6 +199,14 @@ class PosApiTest extends TestCase
|
||||
'warehouse_id' => $warehouseId,
|
||||
'quantity' => 95,
|
||||
]);
|
||||
|
||||
$order = \App\Modules\Integration\Models\SalesOrder::where('external_order_id', 'ORD-001')->first();
|
||||
$this->assertDatabaseHas('inventory_transactions', [
|
||||
'reference_type' => \App\Modules\Integration\Models\SalesOrder::class,
|
||||
'reference_id' => $order->id,
|
||||
'quantity' => -5,
|
||||
'type' => '出庫',
|
||||
]);
|
||||
|
||||
tenancy()->end();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user