146 lines
5.3 KiB
PHP
146 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Services;
|
|
|
|
class ShengsuanOrderProfitDetailMapper
|
|
{
|
|
public const TABLE = 'ops_shengsuan_order_profit_detail_snapshots';
|
|
|
|
private const SHOP_FIELD = 'c_900901';
|
|
|
|
private const ONLINE_ORDER_FIELD = 'c_900902';
|
|
|
|
private const INTERNAL_ORDER_FIELD = 'c_900903';
|
|
|
|
private const BARCODE_FIELD = 'c_900904';
|
|
|
|
private const PRODUCT_COST_FIELD = 'c_900201';
|
|
|
|
public function table(): string
|
|
{
|
|
return self::TABLE;
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $decoded
|
|
* @return list<array<string,mixed>>
|
|
*/
|
|
public function snapshots(array $decoded): array
|
|
{
|
|
$data = is_array($decoded['data'] ?? null) ? $decoded['data'] : $decoded;
|
|
$orders = $this->section($data['ords'] ?? []);
|
|
$afterSales = $this->section($data['afters'] ?? []);
|
|
$groups = [];
|
|
|
|
foreach ([['orders', $orders], ['after_sales', $afterSales]] as [$section, $table]) {
|
|
foreach ($table['dataList'] as $record) {
|
|
$key = $this->orderKey($record);
|
|
if ($key === null) {
|
|
continue;
|
|
}
|
|
$groups[$key] ??= [
|
|
'order_key' => $key,
|
|
'shop_name' => trim((string) ($record[self::SHOP_FIELD] ?? '')),
|
|
'online_order_no' => $this->nullableText($record[self::ONLINE_ORDER_FIELD] ?? null),
|
|
'internal_order_no' => $this->nullableText($record[self::INTERNAL_ORDER_FIELD] ?? null),
|
|
'orders_headers' => $orders['fldHeads'],
|
|
'orders' => [],
|
|
'afters_headers' => $afterSales['fldHeads'],
|
|
'afters' => [],
|
|
];
|
|
$groups[$key][$section === 'orders' ? 'orders' : 'afters'][] = $record;
|
|
}
|
|
}
|
|
|
|
$now = now();
|
|
$snapshots = [];
|
|
foreach ($groups as $group) {
|
|
$missingCostBarcodes = [];
|
|
$missingCostItemCount = 0;
|
|
foreach ($group['orders'] as $item) {
|
|
if (! $this->isZeroOrMissing($item[self::PRODUCT_COST_FIELD] ?? null)) {
|
|
continue;
|
|
}
|
|
$missingCostItemCount++;
|
|
$barcode = $this->nullableText($item[self::BARCODE_FIELD] ?? null);
|
|
if ($barcode !== null && ! in_array($barcode, $missingCostBarcodes, true)) {
|
|
$missingCostBarcodes[] = $barcode;
|
|
}
|
|
}
|
|
|
|
$snapshots[] = [
|
|
'order_key' => $group['order_key'],
|
|
'shop_name' => $group['shop_name'],
|
|
'online_order_no' => $group['online_order_no'],
|
|
'internal_order_no' => $group['internal_order_no'],
|
|
'item_count' => count($group['orders']),
|
|
'after_item_count' => count($group['afters']),
|
|
'missing_cost_item_count' => $missingCostItemCount,
|
|
'order_headers' => $this->json($group['orders_headers']),
|
|
'order_items' => $this->json($group['orders']),
|
|
'after_headers' => $this->json($group['afters_headers']),
|
|
'after_items' => $this->json($group['afters']),
|
|
'missing_cost_barcodes' => $this->json($missingCostBarcodes),
|
|
'synced_at' => $now,
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
];
|
|
}
|
|
|
|
return $snapshots;
|
|
}
|
|
|
|
/** @return array{fldHeads:list<array<string,mixed>>,dataList:list<array<string,mixed>>} */
|
|
private function section(mixed $section): array
|
|
{
|
|
if (! is_array($section)) {
|
|
return ['fldHeads' => [], 'dataList' => []];
|
|
}
|
|
|
|
$headers = is_array($section['fldHeads'] ?? null) ? $section['fldHeads'] : [];
|
|
|
|
return [
|
|
'fldHeads' => array_values(array_map(
|
|
static fn (array $header): array => [
|
|
'fld' => (string) ($header['fld'] ?? ''),
|
|
'title' => (string) ($header['title'] ?? ''),
|
|
'align' => $header['align'] ?? null,
|
|
'sum' => (bool) ($header['sum'] ?? false),
|
|
],
|
|
array_filter($headers, 'is_array'),
|
|
)),
|
|
'dataList' => is_array($section['dataList'] ?? null)
|
|
? array_values(array_filter($section['dataList'], 'is_array'))
|
|
: [],
|
|
];
|
|
}
|
|
|
|
/** @param array<string,mixed> $record */
|
|
private function orderKey(array $record): ?string
|
|
{
|
|
$shop = trim((string) ($record[self::SHOP_FIELD] ?? ''));
|
|
$online = trim((string) ($record[self::ONLINE_ORDER_FIELD] ?? ''));
|
|
$internal = trim((string) ($record[self::INTERNAL_ORDER_FIELD] ?? ''));
|
|
$orderNo = $online !== '' ? $online : $internal;
|
|
|
|
return $shop === '' || $orderNo === '' ? null : $shop . '|' . $orderNo;
|
|
}
|
|
|
|
private function nullableText(mixed $value): ?string
|
|
{
|
|
$text = trim((string) $value);
|
|
|
|
return $text === '' ? null : $text;
|
|
}
|
|
|
|
private function isZeroOrMissing(mixed $value): bool
|
|
{
|
|
return $value === null || trim((string) $value) === '' || (float) $value === 0.0;
|
|
}
|
|
|
|
private function json(mixed $value): string
|
|
{
|
|
return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '[]';
|
|
}
|
|
}
|