Skip to content

Commit 32d660c

Browse files
caothu159Bột
andcommitted
feat(catalog): SO3 hóa đơn bán hàng list + edit CRUD (task 197) (#278)
* feat(catalog): SO3 hóa đơn bán hàng list + edit CRUD (task 197) - Dùng asSoFilt3 cho danh sách, asSOGetPH3/asSOGetCT3 cho load edit - Form nhập/sửa/xóa SO3 theo bộ SP gốc asSOIns/UpdPH3 + CT3 - Route canonical so.vch.sovchso3 + create/edit - Test wrapper/component SO3, cập nhật task docs * fix(catalog): align SO3/PO3 voucher CRUD and SP data sets * feat(catalog): move export to result tab and auto-switch on data * fix(simba): skip empty SQL Server rowsets when reading data sets * fix(catalog): prioritize exact customer name in autocomplete --------- Co-authored-by: Bột <bot@diepxuan.corp>
1 parent 8f1ebd2 commit 32d660c

12 files changed

Lines changed: 511 additions & 56 deletions

src/StoredProcedures/AsPOFilt3.php

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,69 @@
1616
use Diepxuan\Simba\SModel\SModel;
1717
use Illuminate\Support\Collection;
1818
use Diepxuan\Simba\Helper\ParamHelper;
19+
use Illuminate\Support\Facades\DB;
20+
use PDO;
1921

22+
/**
23+
* Stored procedure asPOFilt3.
24+
*
25+
* SP gốc từ SimbaSql:
26+
* - asPOFilt3 (@pKeyPh, @pKeyCt)
27+
* - Dùng cho danh sách hóa đơn mua hàng (PO3).
28+
*/
2029
class AsPOFilt3
2130
{
31+
/**
32+
* Dựng @pKeyPh cho lọc header PO3.
33+
*/
34+
public static function keyPh(string $maCty, string $maCt, ?string $ngay1 = null, ?string $ngay2 = null, ?string $maKh = null, ?string $search = null): string
35+
{
36+
$parts = [
37+
"ma_cty = '" . self::escape($maCty) . "'",
38+
"ma_ct = '" . self::escape($maCt) . "'",
39+
];
40+
41+
if (null !== $ngay1 && '' !== $ngay1) {
42+
$parts[] = "ngay_ct >= '" . self::escape($ngay1) . "'";
43+
}
44+
if (null !== $ngay2 && '' !== $ngay2) {
45+
$parts[] = "ngay_ct <= '" . self::escape($ngay2) . "'";
46+
}
47+
if (null !== $maKh && '' !== $maKh) {
48+
$parts[] = "ma_kh like N'" . self::escape($maKh) . "%'";
49+
}
50+
if (null !== $search && '' !== $search) {
51+
$parts[] = "(so_ct like N'%" . self::escape($search) . "%' or so_hd like N'%" . self::escape($search) . "%' or ma_kh like N'%" . self::escape($search) . "%')";
52+
}
53+
54+
return implode(' and ', $parts);
55+
}
56+
57+
/**
58+
* Dựng @pKeyCt cho lọc chi tiết PO3.
59+
*/
60+
public static function keyCt(string $maCty, ?string $maVt = null, ?string $maKho = null): string
61+
{
62+
$parts = ["ma_cty = '" . self::escape($maCty) . "'"];
63+
64+
if (null !== $maVt && '' !== $maVt) {
65+
$parts[] = "ma_vt = N'" . self::escape($maVt) . "'";
66+
}
67+
if (null !== $maKho && '' !== $maKho) {
68+
$parts[] = "ma_kho = N'" . self::escape($maKho) . "'";
69+
}
70+
71+
return implode(' and ', $parts);
72+
}
73+
74+
/**
75+
* Escape single quote cho SQL Server.
76+
*/
77+
public static function escape(string $value): string
78+
{
79+
return str_replace("'", "''", $value);
80+
}
81+
2282
/**
2383
* Call stored procedure asPOFilt3
2484
*
@@ -30,7 +90,61 @@ public static function call(array $params): Collection
3090
$paramObj = ParamHelper::fromArray($params);
3191
$connection = (new SModel())->getConnectionName();
3292

33-
return ProcedureCaller::call('asPOFilt3', $params, $connection);
93+
return ProcedureCaller::call('asPOFilt3', [
94+
'pKeyPh' => $paramObj->pKeyPh ?? null,
95+
'pKeyCt' => $paramObj->pKeyCt ?? null,
96+
], $connection);
97+
}
98+
99+
/**
100+
* Gọi asPOFilt3 và trả về tất cả result set.
101+
*
102+
* @return array{ph: Collection<int, array<string, mixed>>, ct: Collection<int, array<string, mixed>>}
103+
*/
104+
public static function callWithDataSets(array $params): array
105+
{
106+
$paramObj = ParamHelper::fromArray($params);
107+
$connection = (new SModel())->getConnectionName();
108+
109+
$pdo = DB::connection($connection)->getPdo();
110+
$stmt = $pdo->prepare(self::callSql([
111+
'pKeyPh' => $paramObj->pKeyPh ?? null,
112+
'pKeyCt' => $paramObj->pKeyCt ?? null,
113+
]));
114+
$stmt->execute();
115+
116+
$sets = [];
117+
do {
118+
if ($stmt->columnCount() > 0) {
119+
$sets[] = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
120+
}
121+
} while ($stmt->nextRowset());
122+
123+
try {
124+
$stmt->closeCursor();
125+
} catch (\Throwable) {
126+
// Bỏ qua lỗi closeCursor sau khi đã đọc hết result set.
127+
}
128+
129+
return [
130+
'ph' => collect($sets[0] ?? []),
131+
'ct' => collect($sets[1] ?? []),
132+
];
133+
}
134+
135+
/**
136+
* Build EXEC statement dùng Unicode literal giống ProcedureCaller.
137+
*
138+
* @param array<string, mixed> $procedureParams
139+
*/
140+
public static function callSql(array $procedureParams): string
141+
{
142+
$execParts = [];
143+
foreach ($procedureParams as $key => $value) {
144+
$execParts[] = '@' . $key . ' = ' . ProcedureCaller::toUnicodeLiteral($value);
145+
}
146+
147+
return "SET NOCOUNT ON;\nEXECUTE [dbo].[asPOFilt3]\n " . implode(",\n ", $execParts);
34148
}
35149

36150
/**

src/StoredProcedures/AsPOGetPO3.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Diepxuan\Simba\Helper\ParamHelper;
1717
use Diepxuan\Simba\SModel\SModel;
1818
use Illuminate\Support\Collection;
19+
use Illuminate\Support\Facades\DB;
20+
use PDO;
1921

2022
/**
2123
* Class AsPOGetPO3.
@@ -36,6 +38,54 @@ public static function call(array $params): Collection
3638
], $connection);
3739
}
3840

41+
/**
42+
* Gọi asPOGetPO3 và trả về tất cả result set.
43+
*
44+
* @return array<int, Collection<int, array<string, mixed>>>
45+
*/
46+
public static function callWithDataSets(array $params): array
47+
{
48+
$paramObj = ParamHelper::fromArray($params);
49+
$connection = (new SModel())->getConnectionName();
50+
51+
$pdo = DB::connection($connection)->getPdo();
52+
$stmt = $pdo->prepare(self::callSql([
53+
'pMa_cty' => $paramObj->pMa_cty ?? SModel::CTY,
54+
'pStt_rec' => $paramObj->pStt_rec ?? null,
55+
]));
56+
$stmt->execute();
57+
58+
$sets = [];
59+
do {
60+
if ($stmt->columnCount() > 0) {
61+
$sets[] = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
62+
}
63+
} while ($stmt->nextRowset());
64+
65+
try {
66+
$stmt->closeCursor();
67+
} catch (\Throwable) {
68+
// Bỏ qua lỗi closeCursor sau khi đã đọc hết result set.
69+
}
70+
71+
return array_map(static fn (array $rows): Collection => collect($rows), $sets);
72+
}
73+
74+
/**
75+
* Build EXEC statement dùng Unicode literal giống ProcedureCaller.
76+
*
77+
* @param array<string, mixed> $procedureParams
78+
*/
79+
public static function callSql(array $procedureParams): string
80+
{
81+
$execParts = [];
82+
foreach ($procedureParams as $key => $value) {
83+
$execParts[] = '@' . $key . ' = ' . ProcedureCaller::toUnicodeLiteral($value);
84+
}
85+
86+
return "SET NOCOUNT ON;\nEXECUTE [dbo].[asPOGetPO3]\n " . implode(",\n ", $execParts);
87+
}
88+
3989
public static function callWithParams(?string $maCty = null, ?string $sttRec = null): Collection
4090
{
4191
return self::call([

src/StoredProcedures/AsSODelCT3.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ public static function call(array $params): Collection
3131
$connection = (new SModel())->getConnectionName();
3232

3333
return ProcedureCaller::call('asSODelCT3', [
34-
'pMa_cty' => $paramObj->pMa_cty ?? null,
34+
'pMa_cty' => $paramObj->pMa_cty ?? SModel::CTY,
3535
'pStt_rec' => $paramObj->pStt_rec ?? null,
36-
'pRet' => $paramObj->pRet ?? null
36+
'pRet' => ['type' => 'INT', 'output' => true],
3737
], $connection);
3838
}
3939

src/StoredProcedures/AsSODelPH3.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ public static function call(array $params): Collection
3131
$connection = (new SModel())->getConnectionName();
3232

3333
return ProcedureCaller::call('asSODelPH3', [
34-
'pMa_cty' => $paramObj->pMa_cty ?? null,
34+
'pMa_cty' => $paramObj->pMa_cty ?? SModel::CTY,
3535
'pStt_rec' => $paramObj->pStt_rec ?? null,
36-
'pLUser' => $paramObj->pLUser ?? null,
37-
'pRet' => $paramObj->pRet ?? null
36+
'pLUser' => $paramObj->pLUser ?? (auth()->user()->name ?? 'system'),
37+
'pRet' => ['type' => 'INT', 'output' => true],
3838
], $connection);
3939
}
4040

src/StoredProcedures/AsSOGetCT3.php

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,17 @@
1313

1414
namespace Diepxuan\Simba\StoredProcedures;
1515

16+
use Diepxuan\Simba\Helper\ParamHelper;
1617
use Diepxuan\Simba\SModel\SModel;
1718
use Illuminate\Support\Collection;
18-
use Diepxuan\Simba\Helper\ParamHelper;
19+
20+
/**
21+
* Stored procedure asSOGetCT3.
22+
*
23+
* SP gốc từ SimbaSql:
24+
* - asSOGetCT3 (@pMa_cty, @pStt_rec, @pStruct)
25+
* - Lấy danh sách chi tiết hóa đơn bán hàng (SOCT3).
26+
*/
1927

2028
class AsSOGetCT3
2129
{
@@ -31,28 +39,91 @@ public static function call(array $params): Collection
3139
$connection = (new SModel())->getConnectionName();
3240

3341
return ProcedureCaller::call('asSOGetCT3', [
34-
'pMa_cty' => $paramObj->pMa_cty ?? null,
42+
'pMa_cty' => $paramObj->pMa_cty ?? SModel::CTY,
3543
'pStt_rec' => $paramObj->pStt_rec ?? null,
36-
'pStruct' => $paramObj->pStruct ?? null
44+
'pStruct' => $paramObj->pStruct ?? '0',
3745
], $connection);
3846
}
3947

4048
/**
41-
* Call stored procedure asSOGetCT3 with named parameters
49+
* Chuẩn hóa 1 row chi tiết SO3 về payload Portal.
4250
*
43-
* @param string $Ma_cty
44-
* @param string $Stt_rec
45-
* @param string $Struct
46-
* @return Collection
51+
* @return array<string, mixed>
4752
*/
48-
public static function callWithParams(string $Ma_cty = null, string $Stt_rec = null, string $Struct = null): Collection
53+
public static function normalizeDetail(mixed $row): array
4954
{
50-
$params = [
51-
'pMa_cty' => $Ma_cty,
52-
'pStt_rec' => $Stt_rec,
53-
'pStruct' => $Struct
55+
$row = \is_array($row) ? $row : (array) $row;
56+
57+
return [
58+
'ma_cty' => (string) ($row['ma_cty'] ?? ''),
59+
'stt_rec' => (string) ($row['stt_rec'] ?? ''),
60+
'stt_rec0' => (string) ($row['stt_rec0'] ?? ''),
61+
'stt_rec_dh' => (string) ($row['stt_rec_dh'] ?? ''),
62+
'stt_rec0_dh' => (string) ($row['stt_rec0_dh'] ?? ''),
63+
'stt_rec_px' => (string) ($row['stt_rec_px'] ?? ''),
64+
'stt_rec0_px' => (string) ($row['stt_rec0_px'] ?? ''),
65+
'so_dh' => (string) ($row['so_dh'] ?? ''),
66+
'so_px' => (string) ($row['so_px'] ?? ''),
67+
'ma_vt' => (string) ($row['ma_vt'] ?? ''),
68+
'ten_vt' => (string) ($row['ten_vt'] ?? ''),
69+
'dvt' => (string) ($row['dvt'] ?? ''),
70+
'ton_kho' => (float) ($row['ton_kho'] ?? 0),
71+
'ma_kho' => (string) ($row['ma_kho'] ?? ''),
72+
'ten_kho' => (string) ($row['ten_kho'] ?? ''),
73+
'so_luong' => (float) ($row['so_luong'] ?? 0),
74+
'so_luong_qd' => (float) ($row['so_luong_qd'] ?? 0),
75+
'gia_nt2' => (float) ($row['gia_nt2'] ?? 0),
76+
'gia2' => (float) ($row['gia2'] ?? 0),
77+
'tien_nt2' => (float) ($row['tien_nt2'] ?? 0),
78+
'tien2' => (float) ($row['tien2'] ?? 0),
79+
'tl_ck' => (float) ($row['tl_ck'] ?? 0),
80+
'tien_ck_nt' => (float) ($row['tien_ck_nt'] ?? 0),
81+
'tien_ck' => (float) ($row['tien_ck'] ?? 0),
82+
'ck_ds_nt' => (float) ($row['ck_ds_nt'] ?? 0),
83+
'ck_ds' => (float) ($row['ck_ds'] ?? 0),
84+
'ma_thue' => (string) ($row['ma_thue'] ?? ''),
85+
'ts_gtgt' => (float) ($row['ts_gtgt'] ?? 0),
86+
'thue_gtgt_nt' => (float) ($row['thue_gtgt_nt'] ?? 0),
87+
'thue_gtgt' => (float) ($row['thue_gtgt'] ?? 0),
88+
'tt_nt' => (float) ($row['tt_nt'] ?? 0),
89+
'tt' => (float) ($row['tt'] ?? 0),
90+
'tk_pt' => (string) ($row['tk_pt'] ?? ''),
91+
'tk_thue' => (string) ($row['tk_thue'] ?? ''),
92+
'tk_dt' => (string) ($row['tk_dt'] ?? ''),
93+
'ten_tk_dt' => (string) ($row['ten_tk_dt'] ?? ''),
94+
'tk_gv' => (string) ($row['tk_gv'] ?? ''),
95+
'ten_tk_gv' => (string) ($row['ten_tk_gv'] ?? ''),
96+
'tk_vt' => (string) ($row['tk_vt'] ?? ''),
97+
'ten_tk_vt' => (string) ($row['ten_tk_vt'] ?? ''),
98+
'tk_ck' => (string) ($row['tk_ck'] ?? ''),
99+
'ten_tk_ck' => (string) ($row['ten_tk_ck'] ?? ''),
100+
'tk_tl' => (string) ($row['tk_tl'] ?? ''),
101+
'tk_km' => (string) ($row['tk_km'] ?? ''),
102+
'gia_nt' => (float) ($row['gia_nt'] ?? 0),
103+
'gia' => (float) ($row['gia'] ?? 0),
104+
'tien_nt' => (float) ($row['tien_nt'] ?? 0),
105+
'tien' => (float) ($row['tien'] ?? 0),
106+
'khuyen_mai' => (bool) ($row['khuyen_mai'] ?? false),
107+
'tra_ck' => (bool) ($row['tra_ck'] ?? false),
108+
'sl_xuat' => (float) ($row['sl_xuat'] ?? 0),
109+
'sl_xuat_qd' => (float) ($row['sl_xuat_qd'] ?? 0),
110+
'ma_nvkd' => (string) ($row['ma_nvkd'] ?? ''),
111+
'ten_nvkd' => (string) ($row['ten_nvkd'] ?? ''),
112+
'ma_vitri' => (string) ($row['ma_vitri'] ?? ''),
113+
'ma_lo' => (string) ($row['ma_lo'] ?? ''),
114+
'ma_bp' => (string) ($row['ma_bp'] ?? ''),
115+
'ma_hd' => (string) ($row['ma_hd'] ?? ''),
116+
'ma_phi' => (string) ($row['ma_phi'] ?? ''),
117+
'ma_spct' => (string) ($row['ma_spct'] ?? ''),
54118
];
119+
}
55120

56-
return self::call($params);
121+
public static function callWithParams(?string $maCty = null, ?string $sttRec = null, ?string $struct = null): Collection
122+
{
123+
return self::call([
124+
'pMa_cty' => $maCty,
125+
'pStt_rec' => $sttRec,
126+
'pStruct' => $struct,
127+
]);
57128
}
58129
}

0 commit comments

Comments
 (0)