diff --git a/AMREZ.EOP.API/AMREZ.EOP.API.csproj b/AMREZ.EOP.API/AMREZ.EOP.API.csproj
index be74a5e..efff37b 100644
--- a/AMREZ.EOP.API/AMREZ.EOP.API.csproj
+++ b/AMREZ.EOP.API/AMREZ.EOP.API.csproj
@@ -7,6 +7,7 @@
+
diff --git a/AMREZ.EOP.API/Controllers/DataController.cs b/AMREZ.EOP.API/Controllers/DataController.cs
new file mode 100644
index 0000000..2ebad3f
--- /dev/null
+++ b/AMREZ.EOP.API/Controllers/DataController.cs
@@ -0,0 +1,187 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.ImportData.Location;
+using AMREZ.EOP.Contracts.DTOs.ImportData.Location;
+using AMREZ.EOP.Domain.Shared.Data;
+using ClosedXML.Excel;
+using Microsoft.AspNetCore.Mvc;
+
+namespace AMREZ.EOP.API.Controllers;
+
+public class AddressTemplateRow
+{
+ public string CustomerName { get; set; } = ""; // ชื่อ
+ public string Phone { get; set; } = ""; // เบอร์โทร
+ public string Address { get; set; } = ""; // ที่อยู่ (เลขที่/หมู่/ซอย/ถนน ฯลฯ)
+ public string Subdistrict { get; set; } = ""; // ตำบล
+ public string District { get; set; } = ""; // อำเภอ
+ public string Province { get; set; } = ""; // จังหวัด
+ public string PostalCode { get; set; } = ""; // รหัสไปรษณีย์
+
+ public string? ProductCode { get; set; } // เผื่อใช้ต่อ
+ public string? ProductName { get; set; }
+ public int? Quantity { get; set; }
+}
+
+[ApiController]
+[Route("api/[controller]")]
+public class DataController : ControllerBase
+{
+ private readonly ILocationImportUseCase _locationImportUseCase;
+
+ public DataController(ILocationImportUseCase locationImportUseCase)
+ {
+ _locationImportUseCase = locationImportUseCase;
+ }
+
+ // เซ็ต Fixed สินค้า 3 รายการตามตัวอย่าง
+ private static readonly (string Code, string Name, decimal Price)[] FixedProducts =
+ {
+ ("900-000-01-01", "LAVIESTE (DIETARY SUPPLEMENT PRODUCT) KATHY LABZ BRAND", 1590m),
+ ("900-000-01-02", "KARISTA (DIETARY SUPPLEMENT PRODUCT) KATHY LABZ BRAND", 1590m),
+ ("890-001-00-01", "KATHY LABZ AUTO STIRRING", 390m)
+ };
+
+ [HttpPost("address-parse")]
+ public async Task ThaiParseAddress([FromForm(Name = "Image")] IFormFile? Image)
+ {
+ if (Image == null || Image.Length == 0)
+ return BadRequest("กรุณาอัปโหลดไฟล์ Excel (.xlsx) ที่เป็น 'ข้อมูลจริง ครั้งที่1'");
+
+ using var stream = new MemoryStream();
+ await Image.CopyToAsync(stream);
+ stream.Position = 0;
+
+ using var wb = new XLWorkbook(stream);
+ var ws = wb.Worksheets.First();
+
+ // row1 = header => "username", "Address"
+ var lastRow = ws.LastRowUsed().RowNumber();
+
+ var rows = new List();
+
+ for (int row = 2; row <= lastRow; row++)
+ {
+ var username = ws.Cell(row, 1).GetString().Trim(); // "username"
+ var rawAddress = ws.Cell(row, 2).GetString().Trim(); // "Address"
+
+ if (string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(rawAddress))
+ continue;
+
+ var parsed = ThaiAddressParser.Parse(rawAddress);
+
+ rows.Add(new AddressTemplateRow
+ {
+ CustomerName = string.IsNullOrWhiteSpace(parsed.Name)
+ ? username
+ : parsed.Name,
+ Phone = parsed.Phone,
+ Address = parsed.AddressMain,
+ Subdistrict = parsed.Subdistrict,
+ District = parsed.District,
+ Province = parsed.Province,
+ PostalCode = parsed.PostalCode
+ });
+ }
+
+ // ================== สร้างไฟล์ Excel ตาม Template ==================
+ using var outWb = new XLWorkbook();
+
+ var outWs = outWb.Worksheets.Add("template");
+
+ // Header แถวที่ 1 ให้ตรงกับไฟล์ตัวอย่าง
+ outWs.Cell(1, 1).Value = "สถานะออเดอร์";
+ outWs.Cell(1, 2).Value = "สถานะการชำระเงิน";
+ outWs.Cell(1, 3).Value = "รหัสพนักงานขาย";
+ outWs.Cell(1, 4).Value = "รหัสช่องทางการขาย";
+ outWs.Cell(1, 5).Value = "ช่องทางการชำระเงิน";
+ outWs.Cell(1, 6).Value = "กำหนดส่งสินค้า";
+ outWs.Cell(1, 7).Value = "Order No";
+ outWs.Cell(1, 8).Value = "ชื่อขนส่ง";
+ outWs.Cell(1, 9).Value = "ชื่อ";
+ outWs.Cell(1, 10).Value = "เบอร์โทร";
+ outWs.Cell(1, 11).Value = "E-mail";
+ outWs.Cell(1, 12).Value = "Line";
+ outWs.Cell(1, 13).Value = "Facebook";
+ outWs.Cell(1, 14).Value = "IG";
+ outWs.Cell(1, 15).Value = "หมายเหตุ";
+ outWs.Cell(1, 16).Value = "ที่อยู่";
+ outWs.Cell(1, 17).Value = "ตำบล";
+ outWs.Cell(1, 18).Value = "อำเภอ";
+ outWs.Cell(1, 19).Value = "จังหวัด";
+ outWs.Cell(1, 20).Value = "รหัสไปรษณีย์";
+ outWs.Cell(1, 21).Value = "รหัสสินค้า/รหัสโปรโมชั่น (ตายตัว";
+ outWs.Cell(1, 22).Value = "ชื่อสินค้า/ชื่อโปรโมชั่น (ตายตัว)";
+ outWs.Cell(1, 23).Value = "ราคา/ชิ้น";
+ outWs.Cell(1, 24).Value = "จำนวน";
+ outWs.Cell(1, 25).Value = "ส่วนลด";
+ outWs.Cell(1, 26).Value = "ราคารวมสินค้า";
+ outWs.Cell(1, 27).Value = "ค่าขนส่ง";
+ outWs.Cell(1, 28).Value = "ส่วนลดท้ายบิล";
+ outWs.Cell(1, 29).Value = "ราคารวมออเดอร์";
+ outWs.Cell(1, 30).Value = "Tracking No.";
+ outWs.Cell(1, 31).Value = "วันอนุมัติ";
+
+ var outRow = 2;
+
+ foreach (var r in rows)
+ {
+ // 1 ลูกค้า → 3 แถวสินค้า fix
+ foreach (var p in FixedProducts)
+ {
+ // ข้อมูลลูกค้า
+ outWs.Cell(outRow, 9).Value = r.CustomerName ?? "";
+ outWs.Cell(outRow, 10).Value = r.Phone ?? "";
+ outWs.Cell(outRow, 16).Value = r.Address ?? "";
+ outWs.Cell(outRow, 17).Value = r.Subdistrict ?? "";
+ outWs.Cell(outRow, 18).Value = r.District ?? "";
+ outWs.Cell(outRow, 19).Value = r.Province ?? "";
+ outWs.Cell(outRow, 20).Value = r.PostalCode ?? "";
+
+ // สินค้า
+ outWs.Cell(outRow, 21).Value = p.Code; // รหัสสินค้า
+ outWs.Cell(outRow, 22).Value = p.Name; // ชื่อสินค้า hard-coded
+ outWs.Cell(outRow, 23).Value = p.Price; // ราคา/ชิ้น
+ outWs.Cell(outRow, 24).Value = 1; // จำนวน = 1
+
+ // ส่วนลด/รวม/ค่าขนส่ง/ส่วนลดท้ายบิล/ราคารวมออเดอร์ ปล่อยว่าง
+ // คอลัมน์ 25–31 ไม่เซ็ตอะไร
+
+ outRow++;
+ }
+ }
+
+ outWs.Columns(1, 31).AdjustToContents();
+
+ using var outStream = new MemoryStream();
+ outWb.SaveAs(outStream);
+ outStream.Position = 0;
+
+ var fileName = $"import_SO_template_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
+
+ return File(
+ outStream.ToArray(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ fileName
+ );
+ }
+
+ ///
+ /// Import ตารางจังหวัด / อำเภอ / ตำบล จากไฟล์ Excel (กรมการปกครอง)
+ ///
+ ///
+ /// ส่งไฟล์ .xlsx ผ่าน multipart/form-data, field name = "file"
+ ///
+ [HttpPost("import-th-location")]
+ [Consumes("multipart/form-data")]
+ [ProducesResponseType(typeof(LocationImportResultDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ public async Task ImportLocations(
+ [FromForm(Name = "file")] IFormFile? file,
+ CancellationToken ct)
+ {
+ if (file == null || file.Length == 0)
+ return BadRequest("กรุณาอัปโหลดไฟล์ Excel (.xlsx) รหัสจังหวัด/อำเภอ/ตำบล");
+
+ var result = await _locationImportUseCase.ExecuteAsync(file, ct);
+ return Ok(result);
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.API/Controllers/MasterData/BrandsController.cs b/AMREZ.EOP.API/Controllers/MasterData/BrandsController.cs
new file mode 100644
index 0000000..646f89f
--- /dev/null
+++ b/AMREZ.EOP.API/Controllers/MasterData/BrandsController.cs
@@ -0,0 +1,59 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+using Microsoft.AspNetCore.Mvc;
+
+namespace AMREZ.EOP.API.Controllers.MasterData;
+
+[ApiController]
+[Route("api/master/brands")]
+public sealed class BrandsController : ControllerBase
+{
+ private readonly IListBrandsUseCase _list;
+ private readonly IGetBrandUseCase _get;
+ private readonly ICreateBrandUseCase _create;
+ private readonly IUpdateBrandUseCase _update;
+ private readonly IDeleteBrandUseCase _delete;
+
+ public BrandsController(
+ IListBrandsUseCase list,
+ IGetBrandUseCase get,
+ ICreateBrandUseCase create,
+ IUpdateBrandUseCase update,
+ IDeleteBrandUseCase delete)
+ {
+ _list = list; _get = get; _create = create; _update = update; _delete = delete;
+ }
+
+ [HttpGet]
+ public async Task>> List([FromQuery] BrandListRequest query, CancellationToken ct)
+ => Ok(await _list.ExecuteAsync(query, ct));
+
+ [HttpGet("{id:guid}")]
+ public async Task Get(Guid id, CancellationToken ct)
+ {
+ var res = await _get.ExecuteAsync(id, ct);
+ return res is null ? NotFound() : Ok(res);
+ }
+
+ [HttpPost]
+ public async Task Create([FromBody] BrandCreateRequest body, CancellationToken ct)
+ {
+ var res = await _create.ExecuteAsync(body, ct);
+ return CreatedAtAction(nameof(Get), new { id = res.Id }, res);
+ }
+
+ [HttpPut("{id:guid}")]
+ public async Task Update(Guid id, [FromBody] BrandUpdateRequest body, CancellationToken ct)
+ {
+ var res = await _update.ExecuteAsync(id, body, ct);
+ return res is null ? NotFound() : Ok(res);
+ }
+
+ [HttpDelete("{id:guid}")]
+ public async Task Delete(Guid id, CancellationToken ct)
+ {
+ var ok = await _delete.ExecuteAsync(id, ct);
+ return ok ? NoContent() : NotFound();
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.API/Controllers/SCBController.cs b/AMREZ.EOP.API/Controllers/SCBController.cs
index 0aae1e6..a020bf8 100644
--- a/AMREZ.EOP.API/Controllers/SCBController.cs
+++ b/AMREZ.EOP.API/Controllers/SCBController.cs
@@ -28,13 +28,13 @@ public class SCBController : ControllerBase
{
if (Image is null || Image.Length == 0)
return BadRequest(new { message = "Missing slip image" });
-
+
var res = await _verifyFromImage.ExecuteAsync(new VerifyFromImageRequest
{
Image = Image,
OverrideSendingBank = OverrideSendingBank
}, ct);
-
+
if (res is null) return NotFound(new { message = "Cannot extract transRef or verification failed" });
return Ok(res);
}
diff --git a/AMREZ.EOP.Abstractions/AMREZ.EOP.Abstractions.csproj b/AMREZ.EOP.Abstractions/AMREZ.EOP.Abstractions.csproj
index 0d66818..f1b38eb 100644
--- a/AMREZ.EOP.Abstractions/AMREZ.EOP.Abstractions.csproj
+++ b/AMREZ.EOP.Abstractions/AMREZ.EOP.Abstractions.csproj
@@ -16,7 +16,4 @@
-
-
-
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/ImportData/Location/ILocationImportUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/ImportData/Location/ILocationImportUseCase.cs
new file mode 100644
index 0000000..3596857
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/ImportData/Location/ILocationImportUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.ImportData.Location;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.ImportData.Location;
+
+public interface ILocationImportUseCase
+{
+ Task ExecuteAsync(IFormFile file, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/ICreateAllergenUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/ICreateAllergenUseCase.cs
new file mode 100644
index 0000000..586744f
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/ICreateAllergenUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+
+public interface ICreateAllergenUseCase
+{
+ Task ExecuteAsync(AllergenCreateRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IDeleteAllergenUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IDeleteAllergenUseCase.cs
new file mode 100644
index 0000000..85cac03
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IDeleteAllergenUseCase.cs
@@ -0,0 +1,6 @@
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+
+public interface IDeleteAllergenUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IGetAllergenUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IGetAllergenUseCase.cs
new file mode 100644
index 0000000..73842be
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IGetAllergenUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+
+public interface IGetAllergenUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IListAllergenUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IListAllergenUseCase.cs
new file mode 100644
index 0000000..01885e4
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IListAllergenUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+
+public interface IListAllergenUseCase
+{
+ Task> ExecuteAsync(AllergenListRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IUpdateAllergenUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IUpdateAllergenUseCase.cs
new file mode 100644
index 0000000..18ca9d4
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Allergen/IUpdateAllergenUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+
+public interface IUpdateAllergenUseCase
+{
+ Task ExecuteAsync(Guid id, AllergenUpdateRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/ICreateBrandUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/ICreateBrandUseCase.cs
new file mode 100644
index 0000000..6f6ce86
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/ICreateBrandUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+
+public interface ICreateBrandUseCase
+{
+ Task ExecuteAsync(BrandCreateRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IDeleteBrandUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IDeleteBrandUseCase.cs
new file mode 100644
index 0000000..20a8fe5
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IDeleteBrandUseCase.cs
@@ -0,0 +1,6 @@
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+
+public interface IDeleteBrandUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IGetBrandUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IGetBrandUseCase.cs
new file mode 100644
index 0000000..3a35e7b
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IGetBrandUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+
+public interface IGetBrandUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IListBrandsUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IListBrandsUseCase.cs
new file mode 100644
index 0000000..f7bd9fe
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IListBrandsUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+
+public interface IListBrandsUseCase
+{
+ Task> ExecuteAsync(BrandListRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IUpdateBrandUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IUpdateBrandUseCase.cs
new file mode 100644
index 0000000..db28159
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Brand/IUpdateBrandUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+
+public interface IUpdateBrandUseCase
+{
+ Task ExecuteAsync(Guid id, BrandUpdateRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IGetDistrictUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IGetDistrictUseCase.cs
new file mode 100644
index 0000000..66e6647
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IGetDistrictUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.District;
+
+public interface IGetDistrictUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IListDistrictsUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IListDistrictsUseCase.cs
new file mode 100644
index 0000000..95dce2a
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/District/IListDistrictsUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.District;
+
+public interface IListDistrictsUseCase
+{
+ Task> ExecuteAsync(DistrictListRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IGetProvinceUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IGetProvinceUseCase.cs
new file mode 100644
index 0000000..55703ea
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IGetProvinceUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Province;
+
+public interface IGetProvinceUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IListProvincesUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IListProvincesUseCase.cs
new file mode 100644
index 0000000..5f134f3
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Province/IListProvincesUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Province;
+
+public interface IListProvincesUseCase
+{
+ Task> ExecuteAsync(ProvinceListRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IGetSubdistrictUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IGetSubdistrictUseCase.cs
new file mode 100644
index 0000000..df470c0
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IGetSubdistrictUseCase.cs
@@ -0,0 +1,8 @@
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Subdistrict;
+
+public interface IGetSubdistrictUseCase
+{
+ Task ExecuteAsync(Guid id, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IListSubdistrictsUseCase.cs b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IListSubdistrictsUseCase.cs
new file mode 100644
index 0000000..998c512
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Applications/UseCases/MasterData/Subdistrict/IListSubdistrictsUseCase.cs
@@ -0,0 +1,9 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+
+namespace AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Subdistrict;
+
+public interface IListSubdistrictsUseCase
+{
+ Task> ExecuteAsync(SubdistrictListRequest req, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IAllergenRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IAllergenRepository.cs
new file mode 100644
index 0000000..2dde78d
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IAllergenRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IAllergenRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, AllergenListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Allergen entity, CancellationToken ct = default);
+ Task UpdateAsync(Allergen entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IBrandRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IBrandRepository.cs
new file mode 100644
index 0000000..19d8052
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IBrandRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IBrandRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, BrandListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Brand entity, CancellationToken ct = default);
+ Task UpdateAsync(Brand entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICategoryRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICategoryRepository.cs
new file mode 100644
index 0000000..d2cc323
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICategoryRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Category;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ICategoryRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, CategoryListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Category entity, CancellationToken ct = default);
+ Task UpdateAsync(Category entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IComplianceStatusRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IComplianceStatusRepository.cs
new file mode 100644
index 0000000..dfd37fc
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IComplianceStatusRepository.cs
@@ -0,0 +1,6 @@
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IComplianceStatusRepository
+{
+
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICountryRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICountryRepository.cs
new file mode 100644
index 0000000..4eb6659
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICountryRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Country;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ICountryRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, CountryListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Country entity, CancellationToken ct = default);
+ Task UpdateAsync(Country entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICurrencyRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICurrencyRepository.cs
new file mode 100644
index 0000000..e3d1063
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ICurrencyRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Currency;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ICurrencyRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, CurrencyListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Currency entity, CancellationToken ct = default);
+ Task UpdateAsync(Currency entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDistrictRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDistrictRepository.cs
new file mode 100644
index 0000000..ebab555
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDistrictRepository.cs
@@ -0,0 +1,23 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IDistrictRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+
+ Task> SearchEffectiveAsync(
+ Guid tenantId,
+ DistrictListRequest req,
+ CancellationToken ct = default);
+
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+
+ Task AddAsync(District entity, CancellationToken ct = default);
+
+ Task UpdateAsync(District entity, CancellationToken ct = default);
+
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDocControlStatusRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDocControlStatusRepository.cs
new file mode 100644
index 0000000..cd833b8
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IDocControlStatusRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IDocControlStatusRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, DocControlStatusListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(DocControlStatus entity, CancellationToken ct = default);
+ Task UpdateAsync(DocControlStatus entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IFuncTestRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IFuncTestRepository.cs
new file mode 100644
index 0000000..2274e85
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IFuncTestRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IFuncTestRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, FuncTestListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(FuncTest entity, CancellationToken ct = default);
+ Task UpdateAsync(FuncTest entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IHazardClassRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IHazardClassRepository.cs
new file mode 100644
index 0000000..26baae1
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IHazardClassRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IHazardClassRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, HazardClassListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(HazardClass entity, CancellationToken ct = default);
+ Task UpdateAsync(HazardClass entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ILanguageRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ILanguageRepository.cs
new file mode 100644
index 0000000..a41953b
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ILanguageRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Language;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ILanguageRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, LanguageListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Language entity, CancellationToken ct = default);
+ Task UpdateAsync(Language entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IManufacturerRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IManufacturerRepository.cs
new file mode 100644
index 0000000..cefcfdf
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IManufacturerRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IManufacturerRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, ManufacturerListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Manufacturer entity, CancellationToken ct = default);
+ Task UpdateAsync(Manufacturer entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IMarketRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IMarketRepository.cs
new file mode 100644
index 0000000..61fb728
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IMarketRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Market;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IMarketRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, MarketListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Market entity, CancellationToken ct = default);
+ Task UpdateAsync(Market entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IPackingGroupRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IPackingGroupRepository.cs
new file mode 100644
index 0000000..ad16eb0
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IPackingGroupRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IPackingGroupRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, PackingGroupListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(PackingGroup entity, CancellationToken ct = default);
+ Task UpdateAsync(PackingGroup entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IProvinceRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IProvinceRepository.cs
new file mode 100644
index 0000000..cbdcbef
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IProvinceRepository.cs
@@ -0,0 +1,23 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IProvinceRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+
+ Task> SearchEffectiveAsync(
+ Guid tenantId,
+ ProvinceListRequest req,
+ CancellationToken ct = default);
+
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+
+ Task AddAsync(Province entity, CancellationToken ct = default);
+
+ Task UpdateAsync(Province entity, CancellationToken ct = default);
+
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQaStageRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQaStageRepository.cs
new file mode 100644
index 0000000..e795ab8
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQaStageRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IQaStageRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, QaStageListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(QaStage entity, CancellationToken ct = default);
+ Task UpdateAsync(QaStage entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQcStatusRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQcStatusRepository.cs
new file mode 100644
index 0000000..8ed3a59
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IQcStatusRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IQcStatusRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, QcStatusListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(QcStatus entity, CancellationToken ct = default);
+ Task UpdateAsync(QcStatus entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRecallClassRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRecallClassRepository.cs
new file mode 100644
index 0000000..e1fe83a
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRecallClassRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.RecallClass;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IRecallClassRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, RecallClassListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(RecallClass entity, CancellationToken ct = default);
+ Task UpdateAsync(RecallClass entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRiskClassRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRiskClassRepository.cs
new file mode 100644
index 0000000..d498fef
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRiskClassRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.RiskClass;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IRiskClassRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, RiskClassListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(RiskClass entity, CancellationToken ct = default);
+ Task UpdateAsync(RiskClass entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRouteRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRouteRepository.cs
new file mode 100644
index 0000000..29c2093
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRouteRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Route;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IRouteRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, RouteListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Route entity, CancellationToken ct = default);
+ Task UpdateAsync(Route entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRxScheduleRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRxScheduleRepository.cs
new file mode 100644
index 0000000..f199f41
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IRxScheduleRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.RxSchedule;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IRxScheduleRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, RxScheduleListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(RxSchedule entity, CancellationToken ct = default);
+ Task UpdateAsync(RxSchedule entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISpeciesRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISpeciesRepository.cs
new file mode 100644
index 0000000..48db984
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISpeciesRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Species;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ISpeciesRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, SpeciesListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Species entity, CancellationToken ct = default);
+ Task UpdateAsync(Species entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IStabilityStatusRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IStabilityStatusRepository.cs
new file mode 100644
index 0000000..5aeee85
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IStabilityStatusRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.StabilityStatus;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IStabilityStatusRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, StabilityStatusListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(StabilityStatus entity, CancellationToken ct = default);
+ Task UpdateAsync(StabilityStatus entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISterilizationMethodRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISterilizationMethodRepository.cs
new file mode 100644
index 0000000..52640a2
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISterilizationMethodRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.SterilizationMethod;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ISterilizationMethodRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, SterilizationMethodListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(SterilizationMethod entity, CancellationToken ct = default);
+ Task UpdateAsync(SterilizationMethod entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISubdistrictRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISubdistrictRepository.cs
new file mode 100644
index 0000000..a934290
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/ISubdistrictRepository.cs
@@ -0,0 +1,23 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface ISubdistrictRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+
+ Task> SearchEffectiveAsync(
+ Guid tenantId,
+ SubdistrictListRequest req,
+ CancellationToken ct = default);
+
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+
+ Task AddAsync(Subdistrict entity, CancellationToken ct = default);
+
+ Task UpdateAsync(Subdistrict entity, CancellationToken ct = default);
+
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IUomRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IUomRepository.cs
new file mode 100644
index 0000000..58c2ee8
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IUomRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Uom;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IUomRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, UomListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Uom entity, CancellationToken ct = default);
+ Task UpdateAsync(Uom entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IVvmRepository.cs b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IVvmRepository.cs
new file mode 100644
index 0000000..53e58d8
--- /dev/null
+++ b/AMREZ.EOP.Abstractions/Infrastructures/Repositories/IVvmRepository.cs
@@ -0,0 +1,15 @@
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Vvm;
+using AMREZ.EOP.Domain.Entities.MasterData;
+
+namespace AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+
+public interface IVvmRepository
+{
+ Task GetAsync(Guid id, CancellationToken ct = default);
+ Task> SearchEffectiveAsync(Guid tenantId, VvmListRequest req, CancellationToken ct = default);
+ Task CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default);
+ Task AddAsync(Vvm entity, CancellationToken ct = default);
+ Task UpdateAsync(Vvm entity, CancellationToken ct = default);
+ Task SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/AMREZ.EOP.Application.csproj b/AMREZ.EOP.Application/AMREZ.EOP.Application.csproj
index eef6ff9..d0d9053 100644
--- a/AMREZ.EOP.Application/AMREZ.EOP.Application.csproj
+++ b/AMREZ.EOP.Application/AMREZ.EOP.Application.csproj
@@ -11,13 +11,11 @@
+
-
-
-
diff --git a/AMREZ.EOP.Application/UseCases/Authentications/IssueTokenPairUseCase.cs b/AMREZ.EOP.Application/UseCases/Authentications/IssueTokenPairUseCase.cs
index 0497627..96225e5 100644
--- a/AMREZ.EOP.Application/UseCases/Authentications/IssueTokenPairUseCase.cs
+++ b/AMREZ.EOP.Application/UseCases/Authentications/IssueTokenPairUseCase.cs
@@ -31,72 +31,71 @@ public sealed class IssueTokenPairUseCase : IIssueTokenPairUseCase
_http = http;
}
- public async Task ExecuteAsync(IssueTokenPairRequest request, CancellationToken ct = default)
-{
- var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
-
- var tenantId = request.TenantId;
-
- // ---- สร้าง/บันทึก refresh session ----
- var refreshRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
- var refreshHash = Sha256(refreshRaw);
- var now = DateTimeOffset.UtcNow;
-
- var session = await _users.CreateSessionAsync(new UserSession
+ public async Task ExecuteAsync(IssueTokenPairRequest request,
+ CancellationToken ct = default)
{
- Id = Guid.NewGuid(),
- TenantId = tenantId,
- UserId = request.UserId,
- RefreshTokenHash = refreshHash,
- IssuedAt = now,
- ExpiresAt = now.AddDays(RefreshDays),
- DeviceId = http.Request.Headers["X-Device-Id"].FirstOrDefault(),
- UserAgent = http.Request.Headers["User-Agent"].FirstOrDefault(),
- IpAddress = http.Connection.RemoteIpAddress?.ToString()
- }, ct);
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
- // ---- เวอร์ชัน/สแตมป์ความปลอดภัย ----
- var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct);
- var sstamp = await _users.GetUserSecurityStampAsync(request.UserId, ct) ?? string.Empty;
+ var tenantId = request.TenantId;
- // ---- เตรียม claims พื้นฐาน ----
- var claims = new List
- {
- new("sub", request.UserId.ToString()),
- new("email", request.Email),
- new("tenant", request.Tenant), // tenantKey ที่ FE ใช้
- new("tenant_id", tenantId.ToString()),
- new("sid", session.Id.ToString()),
- new("jti", Guid.NewGuid().ToString("N")),
- new("tv", tv),
- new("sstamp", sstamp),
- new("iat", now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
- };
+ // ---- สร้าง/บันทึก refresh session ----
+ var refreshRaw = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
+ var refreshHash = Sha256(refreshRaw);
+ var now = DateTimeOffset.UtcNow;
- string[] roles = request.Roles ?? Array.Empty();
- if (roles.Length == 0)
- {
- roles = await _users.GetRoleCodesByUserIdAsync(request.UserId, tenantId, ct);
+ var session = await _users.CreateSessionAsync(new UserSession
+ {
+ Id = Guid.NewGuid(),
+ TenantId = tenantId,
+ UserId = request.UserId,
+ RefreshTokenHash = refreshHash,
+ IssuedAt = now,
+ ExpiresAt = now.AddDays(RefreshDays),
+ DeviceId = http.Request.Headers["X-Device-Id"].FirstOrDefault(),
+ UserAgent = http.Request.Headers["User-Agent"].FirstOrDefault(),
+ IpAddress = http.Connection.RemoteIpAddress?.ToString()
+ }, ct);
+
+ var tv = await _users.GetTenantTokenVersionAsync(tenantId, ct);
+ var sstamp = await _users.GetUserSecurityStampAsync(request.UserId, ct) ?? string.Empty;
+
+ // ---- เตรียม claims พื้นฐาน ----
+ var claims = new List
+ {
+ new("sub", request.UserId.ToString()),
+ new("email", request.Email),
+ new("tenant", request.Tenant), // tenantKey ที่ FE ใช้
+ new("tenant_id", tenantId.ToString()),
+ new("sid", session.Id.ToString()),
+ new("jti", Guid.NewGuid().ToString("N")),
+ new("tv", tv),
+ new("sstamp", sstamp),
+ new("iat", now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
+ };
+
+ string[] roles = request.Roles ?? Array.Empty();
+ if (roles.Length == 0)
+ {
+ roles = await _users.GetRoleCodesByUserIdAsync(request.UserId, tenantId, ct);
+ }
+
+ foreach (var r in roles.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct(StringComparer.OrdinalIgnoreCase))
+ {
+ claims.Add(new Claim("role", r));
+ claims.Add(new Claim(ClaimTypes.Role, r));
+ }
+
+ var (access, accessExp) = _jwt.CreateAccessToken(claims);
+
+ return new IssueTokenPairResponse
+ {
+ AccessToken = access,
+ AccessExpiresAt = accessExp,
+ RefreshToken = refreshRaw,
+ RefreshExpiresAt = session.ExpiresAt
+ };
}
- foreach (var r in roles.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct(StringComparer.OrdinalIgnoreCase))
- {
- claims.Add(new Claim("role", r));
- claims.Add(new Claim(ClaimTypes.Role, r));
- }
-
- // ---- ออก access token ----
- var (access, accessExp) = _jwt.CreateAccessToken(claims);
-
- return new IssueTokenPairResponse
- {
- AccessToken = access,
- AccessExpiresAt = accessExp,
- RefreshToken = refreshRaw,
- RefreshExpiresAt = session.ExpiresAt
- };
-}
-
private static string Sha256(string raw)
{
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(raw));
diff --git a/AMREZ.EOP.Application/UseCases/ImportData/Location/LocationImportUseCase.cs b/AMREZ.EOP.Application/UseCases/ImportData/Location/LocationImportUseCase.cs
new file mode 100644
index 0000000..091b6eb
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/ImportData/Location/LocationImportUseCase.cs
@@ -0,0 +1,413 @@
+using System.Data;
+using System.Text;
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.ImportData.Location;
+using AMREZ.EOP.Abstractions.Infrastructures.Common;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.ImportData.Location;
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+using AMREZ.EOP.Domain.Entities.MasterData;
+using AMREZ.EOP.Domain.Entities.Tenancy;
+using ClosedXML.Excel;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.ImportData.Location;
+
+public sealed class LocationImportUseCase : ILocationImportUseCase
+{
+ private readonly IProvinceRepository _provinceRepo;
+ private readonly IDistrictRepository _districtRepo;
+ private readonly ISubdistrictRepository _subdistrictRepo;
+ private readonly ITenantRepository _tenants;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+ private readonly IUnitOfWork _uow;
+
+ public LocationImportUseCase(
+ IProvinceRepository provinceRepo,
+ IDistrictRepository districtRepo,
+ ISubdistrictRepository subdistrictRepo,
+ ITenantRepository tenants,
+ ITenantResolver tenantResolver,
+ IHttpContextAccessor http,
+ IUnitOfWork uow)
+ {
+ _provinceRepo = provinceRepo;
+ _districtRepo = districtRepo;
+ _subdistrictRepo = subdistrictRepo;
+ _tenants = tenants;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ _uow = uow;
+ }
+
+ public async Task ExecuteAsync(IFormFile file, CancellationToken ct = default)
+ {
+ if (file == null || file.Length == 0)
+ throw new InvalidOperationException("Excel file is required");
+
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+
+ var tn = await _tenants.GetAsync(tc.Id) ?? throw new InvalidOperationException("Tenant config not found");
+ var tenantId = tn.TenantId;
+
+ var result = new LocationImportResultDto();
+
+ await _uow.BeginAsync(tc, IsolationLevel.ReadCommitted, ct);
+ try
+ {
+ var provincesByCode = await LoadGlobalProvincesAsync(tenantId, ct);
+ var districtsByCode = await LoadGlobalDistrictsAsync(tenantId, ct);
+ var subdistrictsByCode = await LoadGlobalSubdistrictsAsync(tenantId, ct);
+
+ using var stream = new MemoryStream();
+ await file.CopyToAsync(stream, ct);
+ stream.Position = 0;
+
+ using var wb = new XLWorkbook(stream);
+ var ws = wb.Worksheets.First();
+ var lastRow = ws.LastRowUsed().RowNumber();
+
+ // row 1 = header
+ for (var row = 2; row <= lastRow; row++)
+ {
+ ct.ThrowIfCancellationRequested();
+ result.RowsRead++;
+
+ string Get(int col)
+ {
+ try
+ {
+ return ws.Cell(row, col).GetString().Trim();
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+
+ // 1: CC, 2: AA, 3: TT, 4: CCAATT, 5: จังหวัด, 6: อำเภอ, 7: ตำบล, 8: รหัสไปรษณีย์
+ var cc = Get(1);
+ var aa = Get(2);
+ var tt = Get(3);
+ var locCode = Get(4);
+
+ var provinceName = Get(5);
+ var districtName = Get(6);
+ var subdistrictName = Get(7);
+ var postcode = Get(8);
+
+ string? code = null;
+
+ if (!string.IsNullOrWhiteSpace(cc) &&
+ !string.IsNullOrWhiteSpace(aa) &&
+ !string.IsNullOrWhiteSpace(tt))
+ {
+ code =
+ cc.PadLeft(2, '0') +
+ aa.PadLeft(2, '0') +
+ tt.PadLeft(2, '0');
+ }
+ else if (!string.IsNullOrWhiteSpace(locCode))
+ {
+ code = locCode;
+ }
+
+ if (string.IsNullOrWhiteSpace(code))
+ continue;
+
+ code = code.Trim();
+ if (code.Length < 6)
+ code = code.PadLeft(6, '0');
+ else if (code.Length > 6)
+ code = code[..6];
+
+ var isProvince = code.EndsWith("0000", StringComparison.Ordinal);
+ var isDistrict = !isProvince && code.EndsWith("00", StringComparison.Ordinal);
+ var isSubdistrict = !isProvince && !isDistrict;
+
+ if (isProvince)
+ {
+ await ImportProvinceAsync(code, provinceName, provincesByCode, tenantId, result, ct);
+ }
+ else if (isDistrict)
+ {
+ await ImportDistrictAsync(
+ code,
+ provinceName,
+ districtName,
+ provincesByCode,
+ districtsByCode,
+ tenantId,
+ result,
+ ct);
+ }
+ else if (isSubdistrict)
+ {
+ await ImportSubdistrictAsync(
+ code,
+ provinceName,
+ districtName,
+ subdistrictName,
+ postcode,
+ provincesByCode,
+ districtsByCode,
+ subdistrictsByCode,
+ tenantId,
+ result,
+ ct);
+ }
+ }
+
+ await _uow.CommitAsync(ct);
+ return result;
+ }
+ catch
+ {
+ await _uow.RollbackAsync(ct);
+ throw;
+ }
+ }
+
+ // ========== preload helpers ==========
+
+ private async Task> LoadGlobalProvincesAsync(Guid tenantId, CancellationToken ct)
+ {
+ var req = new ProvinceListRequest
+ {
+ Page = 1,
+ PageSize = 500,
+ IncludeInactive = true,
+ Search = null
+ };
+
+ var page = await _provinceRepo.SearchEffectiveAsync(tenantId, req, ct);
+
+ return page.Items
+ .Where(x => x.Scope == "global" && !string.IsNullOrWhiteSpace(x.Code))
+ .GroupBy(x => x.Code!)
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ private async Task> LoadGlobalDistrictsAsync(Guid tenantId, CancellationToken ct)
+ {
+ var req = new DistrictListRequest
+ {
+ Page = 1,
+ PageSize = 5000,
+ IncludeInactive = true,
+ Search = null
+ };
+
+ var page = await _districtRepo.SearchEffectiveAsync(tenantId, req, ct);
+
+ return page.Items
+ .Where(x => x.Scope == "global" && !string.IsNullOrWhiteSpace(x.Code))
+ .GroupBy(x => x.Code!)
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ private async Task> LoadGlobalSubdistrictsAsync(Guid tenantId, CancellationToken ct)
+ {
+ var req = new SubdistrictListRequest
+ {
+ Page = 1,
+ PageSize = 10000,
+ IncludeInactive = true,
+ Search = null
+ };
+
+ var page = await _subdistrictRepo.SearchEffectiveAsync(tenantId, req, ct);
+
+ return page.Items
+ .Where(x => x.Scope == "global" && !string.IsNullOrWhiteSpace(x.Code))
+ .GroupBy(x => x.Code!)
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ // ========== import helpers ==========
+
+ private async Task ImportProvinceAsync(
+ string ccaatt,
+ string? provinceName,
+ Dictionary provincesByCode,
+ Guid tenantId,
+ LocationImportResultDto result,
+ CancellationToken ct)
+ {
+ var provCode = ccaatt[..2];
+
+ if (!provincesByCode.TryGetValue(provCode, out var prov))
+ {
+ prov = new Province
+ {
+ Id = Guid.NewGuid(),
+ TenantId = tenantId,
+ Scope = "global",
+ Code = provCode,
+ Name = string.IsNullOrWhiteSpace(provinceName) ? provCode : provinceName,
+ IsActive = true,
+ IsSystem = true
+ };
+
+ await _provinceRepo.AddAsync(prov, ct);
+ provincesByCode[provCode] = prov;
+ result.ProvincesCreated++;
+ return;
+ }
+
+ var updated = false;
+
+ if (!string.IsNullOrWhiteSpace(provinceName) && prov.Name != provinceName)
+ {
+ prov.Name = provinceName;
+ updated = true;
+ }
+
+ if (updated)
+ {
+ await _provinceRepo.UpdateAsync(prov, ct);
+ result.ProvincesUpdated++;
+ }
+ }
+
+ private async Task ImportDistrictAsync(
+ string ccaatt,
+ string? provinceName,
+ string? districtName,
+ Dictionary provincesByCode,
+ Dictionary districtsByCode,
+ Guid tenantId,
+ LocationImportResultDto result,
+ CancellationToken ct)
+ {
+ var provCode = ccaatt[..2];
+ var distCode = ccaatt[..4];
+
+ if (!provincesByCode.TryGetValue(provCode, out var prov))
+ {
+ await ImportProvinceAsync(provCode + "0000", provinceName, provincesByCode, tenantId, result, ct);
+ prov = provincesByCode[provCode];
+ }
+
+ if (!districtsByCode.TryGetValue(distCode, out var dist))
+ {
+ dist = new District
+ {
+ Id = Guid.NewGuid(),
+ TenantId = tenantId,
+ Scope = "global",
+ Code = distCode,
+ Name = string.IsNullOrWhiteSpace(districtName) ? distCode : districtName,
+ ProvinceId = prov.Id,
+ IsActive = true,
+ IsSystem = true
+ };
+
+ await _districtRepo.AddAsync(dist, ct);
+ districtsByCode[distCode] = dist;
+ result.DistrictsCreated++;
+ return;
+ }
+
+ var updated = false;
+
+ if (!string.IsNullOrWhiteSpace(districtName) && dist.Name != districtName)
+ {
+ dist.Name = districtName;
+ updated = true;
+ }
+
+ if (dist.ProvinceId != prov.Id)
+ {
+ dist.ProvinceId = prov.Id;
+ updated = true;
+ }
+
+ if (updated)
+ {
+ await _districtRepo.UpdateAsync(dist, ct);
+ result.DistrictsUpdated++;
+ }
+ }
+
+ private async Task ImportSubdistrictAsync(
+ string ccaatt,
+ string? provinceName,
+ string? districtName,
+ string? subdistrictName,
+ string? postcode,
+ Dictionary provincesByCode,
+ Dictionary districtsByCode,
+ Dictionary subdistrictsByCode,
+ Guid tenantId,
+ LocationImportResultDto result,
+ CancellationToken ct)
+ {
+ var provCode = ccaatt[..2];
+ var distCode = ccaatt[..4];
+ var subCode = ccaatt[..6];
+
+ if (!provincesByCode.TryGetValue(provCode, out var prov))
+ {
+ await ImportProvinceAsync(provCode + "0000", provinceName, provincesByCode, tenantId, result, ct);
+ prov = provincesByCode[provCode];
+ }
+
+ if (!districtsByCode.TryGetValue(distCode, out var dist))
+ {
+ await ImportDistrictAsync(distCode + "00", provinceName, districtName, provincesByCode, districtsByCode, tenantId, result, ct);
+ dist = districtsByCode[distCode];
+ }
+
+ if (!subdistrictsByCode.TryGetValue(subCode, out var sub))
+ {
+ sub = new Subdistrict
+ {
+ Id = Guid.NewGuid(),
+ TenantId = tenantId,
+ Scope = "global",
+ Code = subCode,
+ Name = string.IsNullOrWhiteSpace(subdistrictName) ? subCode : subdistrictName,
+ DistrictId = dist.Id,
+ Postcode = string.IsNullOrWhiteSpace(postcode) ? null : postcode,
+ IsActive = true,
+ IsSystem = true
+ };
+
+ await _subdistrictRepo.AddAsync(sub, ct);
+ subdistrictsByCode[subCode] = sub;
+ result.SubdistrictsCreated++;
+ return;
+ }
+
+ var updated = false;
+
+ if (!string.IsNullOrWhiteSpace(subdistrictName) && sub.Name != subdistrictName)
+ {
+ sub.Name = subdistrictName;
+ updated = true;
+ }
+
+ if (sub.DistrictId != dist.Id)
+ {
+ sub.DistrictId = dist.Id;
+ updated = true;
+ }
+
+ if (!string.IsNullOrWhiteSpace(postcode) && sub.Postcode != postcode)
+ {
+ sub.Postcode = postcode;
+ updated = true;
+ }
+
+ if (updated)
+ {
+ await _subdistrictRepo.UpdateAsync(sub, ct);
+ result.SubdistrictsUpdated++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Allergen/CreateAllergenUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/CreateAllergenUseCase.cs
new file mode 100644
index 0000000..1527654
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/CreateAllergenUseCase.cs
@@ -0,0 +1,60 @@
+using System.Data;
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+using AMREZ.EOP.Abstractions.Infrastructures.Common;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Allergen;
+
+public sealed class CreateAllergenUseCase : ICreateAllergenUseCase
+{
+ private readonly IAllergenRepository _repo;
+ private readonly IUnitOfWork _uow;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public CreateAllergenUseCase(IAllergenRepository repo, IUnitOfWork uow, ITenantResolver tr, IHttpContextAccessor http)
+ {
+ _repo = repo; _uow = uow; _tenantResolver = tr; _http = http;
+ }
+
+ public async Task ExecuteAsync(AllergenCreateRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+
+ await _uow.BeginAsync(tc, IsolationLevel.ReadCommitted, ct);
+ try
+ {
+ var tid = Guid.Parse(tc.Id);
+
+ if (req.Scope == "tenant" && await _repo.CodeExistsAsync(tid, req.Code, ct))
+ throw new InvalidOperationException($"Brand code '{req.Code}' already exists.");
+
+ var entity = new Domain.Entities.MasterData.Allergen()
+ {
+ TenantId = tid,
+ Scope = string.IsNullOrWhiteSpace(req.Scope) ? "tenant" : req.Scope,
+ Code = req.Code.Trim(),
+ Name = req.Name.Trim(),
+ NameI18n = req.NameI18n,
+ Meta = req.Meta,
+ IsActive = req.IsActive,
+ IsSystem = false,
+ OverridesGlobalId = req.OverridesGlobalId
+ };
+
+ await _repo.AddAsync(entity, ct);
+ await _uow.CommitAsync(ct);
+
+ return new AllergenResponse(entity.Id, entity.Scope, entity.Code, entity.Name, entity.NameI18n, entity.OverridesGlobalId, entity.IsActive, entity.IsSystem, entity.Meta);
+ }
+ catch
+ {
+ await _uow.RollbackAsync(ct);
+ throw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Allergen/DeleteAllergenUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/DeleteAllergenUseCase.cs
new file mode 100644
index 0000000..6ec40a3
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/DeleteAllergenUseCase.cs
@@ -0,0 +1,30 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Allergen;
+
+public class DeleteAllergenUseCase : IDeleteAllergenUseCase
+{
+ private readonly IAllergenRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public DeleteAllergenUseCase(IAllergenRepository repo, ITenantResolver tr, IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tr;
+ _http = http;
+ }
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var n = await _repo.SoftDeleteAsync(id, tid, ct);
+ return n > 0;
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Allergen/GetAllergenUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/GetAllergenUseCase.cs
new file mode 100644
index 0000000..d7d30c1
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/GetAllergenUseCase.cs
@@ -0,0 +1,17 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Allergen;
+
+public class GetAllergenUseCase : IGetAllergenUseCase
+{
+ private readonly IAllergenRepository _repo;
+ public GetAllergenUseCase(IAllergenRepository repo) => _repo = repo;
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var e = await _repo.GetAsync(id, ct);
+ return e is null ? null : new AllergenResponse(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Allergen/ListAllergenUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/ListAllergenUseCase.cs
new file mode 100644
index 0000000..7018c5c
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/ListAllergenUseCase.cs
@@ -0,0 +1,36 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Allergen;
+
+public class ListAllergenUseCase : IListAllergenUseCase
+{
+ private readonly IAllergenRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public ListAllergenUseCase(IAllergenRepository repo, ITenantResolver tenantResolver, IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ }
+
+ public async Task> ExecuteAsync(AllergenListRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var page = await _repo.SearchEffectiveAsync(tid, req, ct);
+ var items = page.Items.Select(Map).ToList();
+ return new PagedResponse(page.Page, page.PageSize, page.Total, items);
+ }
+
+ private static AllergenResponse Map(Domain.Entities.MasterData.Allergen e) =>
+ new(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Allergen/UpdateAllergenUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/UpdateAllergenUseCase.cs
new file mode 100644
index 0000000..cbe4963
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Allergen/UpdateAllergenUseCase.cs
@@ -0,0 +1,40 @@
+using System.Data;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Allergen;
+using AMREZ.EOP.Abstractions.Infrastructures.Common;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Allergen;
+
+public class UpdateAllergenUseCase : IUpdateAllergenUseCase
+{
+ private readonly IAllergenRepository _repo;
+ private readonly IUnitOfWork _uow;
+
+ public UpdateAllergenUseCase(IAllergenRepository repo, IUnitOfWork uow) { _repo = repo; _uow = uow; }
+
+ public async Task ExecuteAsync(Guid id, AllergenUpdateRequest req, CancellationToken ct = default)
+ {
+ await _uow.BeginAsync(null, IsolationLevel.ReadCommitted, ct);
+ try
+ {
+ var e = await _repo.GetAsync(id, ct);
+ if (e is null) { await _uow.RollbackAsync(ct); return null; }
+
+ e.Name = req.Name.Trim();
+ e.NameI18n = req.NameI18n;
+ e.Meta = req.Meta;
+ e.IsActive = req.IsActive;
+
+ await _repo.UpdateAsync(e, ct);
+ await _uow.CommitAsync(ct);
+
+ return new AllergenResponse(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+ }
+ catch
+ {
+ await _uow.RollbackAsync(ct);
+ throw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Brand/CreateBrandUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Brand/CreateBrandUseCase.cs
new file mode 100644
index 0000000..ed3c101
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Brand/CreateBrandUseCase.cs
@@ -0,0 +1,60 @@
+using System.Data;
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Abstractions.Infrastructures.Common;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Brand;
+
+public sealed class CreateBrandUseCase : ICreateBrandUseCase
+{
+ private readonly IBrandRepository _repo;
+ private readonly IUnitOfWork _uow;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public CreateBrandUseCase(IBrandRepository repo, IUnitOfWork uow, ITenantResolver tr, IHttpContextAccessor http)
+ {
+ _repo = repo; _uow = uow; _tenantResolver = tr; _http = http;
+ }
+
+ public async Task ExecuteAsync(BrandCreateRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+
+ await _uow.BeginAsync(tc, IsolationLevel.ReadCommitted, ct);
+ try
+ {
+ var tid = Guid.Parse(tc.Id);
+
+ if (req.Scope == "tenant" && await _repo.CodeExistsAsync(tid, req.Code, ct))
+ throw new InvalidOperationException($"Brand code '{req.Code}' already exists.");
+
+ var entity = new Domain.Entities.MasterData.Brand
+ {
+ TenantId = tid,
+ Scope = string.IsNullOrWhiteSpace(req.Scope) ? "tenant" : req.Scope,
+ Code = req.Code.Trim(),
+ Name = req.Name.Trim(),
+ NameI18n = req.NameI18n,
+ Meta = req.Meta,
+ IsActive = req.IsActive,
+ IsSystem = false,
+ OverridesGlobalId = req.OverridesGlobalId
+ };
+
+ await _repo.AddAsync(entity, ct);
+ await _uow.CommitAsync(ct);
+
+ return new BrandResponse(entity.Id, entity.Scope, entity.Code, entity.Name, entity.NameI18n, entity.OverridesGlobalId, entity.IsActive, entity.IsSystem, entity.Meta);
+ }
+ catch
+ {
+ await _uow.RollbackAsync(ct);
+ throw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Brand/DeleteBrandUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Brand/DeleteBrandUseCase.cs
new file mode 100644
index 0000000..6b67d6e
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Brand/DeleteBrandUseCase.cs
@@ -0,0 +1,30 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Brand;
+
+public sealed class DeleteBrandUseCase : IDeleteBrandUseCase
+{
+ private readonly IBrandRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public DeleteBrandUseCase(IBrandRepository repo, ITenantResolver tr, IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tr;
+ _http = http;
+ }
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var n = await _repo.SoftDeleteAsync(id, tid, ct);
+ return n > 0;
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Brand/GetBrandUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Brand/GetBrandUseCase.cs
new file mode 100644
index 0000000..dc9e434
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Brand/GetBrandUseCase.cs
@@ -0,0 +1,17 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Brand;
+
+public sealed class GetBrandUseCase : IGetBrandUseCase
+{
+ private readonly IBrandRepository _repo;
+ public GetBrandUseCase(IBrandRepository repo) => _repo = repo;
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var e = await _repo.GetAsync(id, ct);
+ return e is null ? null : new BrandResponse(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Brand/ListBrandsUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Brand/ListBrandsUseCase.cs
new file mode 100644
index 0000000..1906967
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Brand/ListBrandsUseCase.cs
@@ -0,0 +1,36 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Brand;
+
+public sealed class ListBrandsUseCase : IListBrandsUseCase
+{
+ private readonly IBrandRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public ListBrandsUseCase(IBrandRepository repo, ITenantResolver tenantResolver, IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ }
+
+ public async Task> ExecuteAsync(BrandListRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var page = await _repo.SearchEffectiveAsync(tid, req, ct);
+ var items = page.Items.Select(Map).ToList();
+ return new PagedResponse(page.Page, page.PageSize, page.Total, items);
+ }
+
+ private static BrandResponse Map(Domain.Entities.MasterData.Brand e) =>
+ new(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+}
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Brand/UpdateBrandUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Brand/UpdateBrandUseCase.cs
new file mode 100644
index 0000000..0a0036f
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Brand/UpdateBrandUseCase.cs
@@ -0,0 +1,40 @@
+using System.Data;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Brand;
+using AMREZ.EOP.Abstractions.Infrastructures.Common;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Brand;
+
+public sealed class UpdateBrandUseCase : IUpdateBrandUseCase
+{
+ private readonly IBrandRepository _repo;
+ private readonly IUnitOfWork _uow;
+
+ public UpdateBrandUseCase(IBrandRepository repo, IUnitOfWork uow) { _repo = repo; _uow = uow; }
+
+ public async Task ExecuteAsync(Guid id, BrandUpdateRequest req, CancellationToken ct = default)
+ {
+ await _uow.BeginAsync(null, IsolationLevel.ReadCommitted, ct);
+ try
+ {
+ var e = await _repo.GetAsync(id, ct);
+ if (e is null) { await _uow.RollbackAsync(ct); return null; }
+
+ e.Name = req.Name.Trim();
+ e.NameI18n = req.NameI18n;
+ e.Meta = req.Meta;
+ e.IsActive = req.IsActive;
+
+ await _repo.UpdateAsync(e, ct);
+ await _uow.CommitAsync(ct);
+
+ return new BrandResponse(e.Id, e.Scope, e.Code, e.Name, e.NameI18n, e.OverridesGlobalId, e.IsActive, e.IsSystem, e.Meta);
+ }
+ catch
+ {
+ await _uow.RollbackAsync(ct);
+ throw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/District/GetDistrictUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/District/GetDistrictUseCase.cs
new file mode 100644
index 0000000..6dcce27
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/District/GetDistrictUseCase.cs
@@ -0,0 +1,37 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.District;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.District;
+
+public sealed class GetDistrictUseCase : IGetDistrictUseCase
+{
+ private readonly IDistrictRepository _repo;
+
+ public GetDistrictUseCase(IDistrictRepository repo)
+ {
+ _repo = repo;
+ }
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var e = await _repo.GetAsync(id, ct);
+ if (e is null) return null;
+
+ return new DistrictResponse(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code,
+ e.ProvinceId,
+ e.Province.Name,
+ e.Province.Code
+ );
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/District/ListDistrictsUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/District/ListDistrictsUseCase.cs
new file mode 100644
index 0000000..f22f9e9
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/District/ListDistrictsUseCase.cs
@@ -0,0 +1,54 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.District;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.District;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.District;
+
+public sealed class ListDistrictsUseCase : IListDistrictsUseCase
+{
+ private readonly IDistrictRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public ListDistrictsUseCase(
+ IDistrictRepository repo,
+ ITenantResolver tenantResolver,
+ IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ }
+
+ public async Task> ExecuteAsync(DistrictListRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var page = await _repo.SearchEffectiveAsync(tid, req, ct);
+ var items = page.Items.Select(Map).ToList();
+
+ return new PagedResponse(page.Page, page.PageSize, page.Total, items);
+ }
+
+ private static DistrictResponse Map(Domain.Entities.MasterData.District e) =>
+ new(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code,
+ e.ProvinceId,
+ e.Province.Name,
+ e.Province.Code
+ );
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Province/GetProvinceUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Province/GetProvinceUseCase.cs
new file mode 100644
index 0000000..603d001
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Province/GetProvinceUseCase.cs
@@ -0,0 +1,34 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Province;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Province;
+
+public sealed class GetProvinceUseCase : IGetProvinceUseCase
+{
+ private readonly IProvinceRepository _repo;
+
+ public GetProvinceUseCase(IProvinceRepository repo)
+ {
+ _repo = repo;
+ }
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var e = await _repo.GetAsync(id, ct);
+ if (e is null) return null;
+
+ return new ProvinceResponse(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code
+ );
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Province/ListProvincesUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Province/ListProvincesUseCase.cs
new file mode 100644
index 0000000..a3687b3
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Province/ListProvincesUseCase.cs
@@ -0,0 +1,51 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Province;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Province;
+
+public sealed class ListProvincesUseCase : IListProvincesUseCase
+{
+ private readonly IProvinceRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public ListProvincesUseCase(
+ IProvinceRepository repo,
+ ITenantResolver tenantResolver,
+ IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ }
+
+ public async Task> ExecuteAsync(ProvinceListRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var page = await _repo.SearchEffectiveAsync(tid, req, ct);
+ var items = page.Items.Select(Map).ToList();
+
+ return new PagedResponse(page.Page, page.PageSize, page.Total, items);
+ }
+
+ private static ProvinceResponse Map(Domain.Entities.MasterData.Province e) =>
+ new(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code
+ );
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/GetSubdistrictUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/GetSubdistrictUseCase.cs
new file mode 100644
index 0000000..80a80d1
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/GetSubdistrictUseCase.cs
@@ -0,0 +1,41 @@
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Subdistrict;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Subdistricts;
+
+public sealed class GetSubdistrictUseCase : IGetSubdistrictUseCase
+{
+ private readonly ISubdistrictRepository _repo;
+
+ public GetSubdistrictUseCase(ISubdistrictRepository repo)
+ {
+ _repo = repo;
+ }
+
+ public async Task ExecuteAsync(Guid id, CancellationToken ct = default)
+ {
+ var e = await _repo.GetAsync(id, ct);
+ if (e is null) return null;
+
+ return new SubdistrictResponse(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code,
+ e.Postcode,
+ e.DistrictId,
+ e.District.Name,
+ e.District.Code,
+ e.District.ProvinceId,
+ e.District.Province.Name,
+ e.District.Province.Code
+ );
+ }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/ListSubdistrictsUseCase.cs b/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/ListSubdistrictsUseCase.cs
new file mode 100644
index 0000000..e7da4ec
--- /dev/null
+++ b/AMREZ.EOP.Application/UseCases/MasterData/Subdistricts/ListSubdistrictsUseCase.cs
@@ -0,0 +1,58 @@
+using AMREZ.EOP.Abstractions.Applications.Tenancy;
+using AMREZ.EOP.Abstractions.Applications.UseCases.MasterData.Subdistrict;
+using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
+using AMREZ.EOP.Contracts.DTOs.Common;
+using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
+using Microsoft.AspNetCore.Http;
+
+namespace AMREZ.EOP.Application.UseCases.MasterData.Subdistricts;
+
+public sealed class ListSubdistrictsUseCase : IListSubdistrictsUseCase
+{
+ private readonly ISubdistrictRepository _repo;
+ private readonly ITenantResolver _tenantResolver;
+ private readonly IHttpContextAccessor _http;
+
+ public ListSubdistrictsUseCase(
+ ISubdistrictRepository repo,
+ ITenantResolver tenantResolver,
+ IHttpContextAccessor http)
+ {
+ _repo = repo;
+ _tenantResolver = tenantResolver;
+ _http = http;
+ }
+
+ public async Task> ExecuteAsync(SubdistrictListRequest req, CancellationToken ct = default)
+ {
+ var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
+ var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
+ var tid = Guid.Parse(tc.Id);
+
+ var page = await _repo.SearchEffectiveAsync(tid, req, ct);
+ var items = page.Items.Select(Map).ToList();
+
+ return new PagedResponse(page.Page, page.PageSize, page.Total, items);
+ }
+
+ private static SubdistrictResponse Map(Domain.Entities.MasterData.Subdistrict e) =>
+ new(
+ e.Id,
+ e.Scope,
+ e.Code,
+ e.Name,
+ e.NameI18n,
+ e.OverridesGlobalId,
+ e.IsActive,
+ e.IsSystem,
+ e.Meta,
+ e.Code,
+ e.Postcode,
+ e.DistrictId,
+ e.District.Name,
+ e.District.Code,
+ e.District.ProvinceId,
+ e.District.Province.Name,
+ e.District.Province.Code
+ );
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/Common/PagedResponse.cs b/AMREZ.EOP.Contracts/DTOs/Common/PagedResponse.cs
new file mode 100644
index 0000000..37bea5e
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/Common/PagedResponse.cs
@@ -0,0 +1,3 @@
+namespace AMREZ.EOP.Contracts.DTOs.Common;
+
+public sealed record PagedResponse(int Page, int PageSize, int Total, IReadOnlyList Items);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/ImportData/Location/LocationImportResultDto.cs b/AMREZ.EOP.Contracts/DTOs/ImportData/Location/LocationImportResultDto.cs
new file mode 100644
index 0000000..566b9bc
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/ImportData/Location/LocationImportResultDto.cs
@@ -0,0 +1,15 @@
+namespace AMREZ.EOP.Contracts.DTOs.ImportData.Location;
+
+public sealed class LocationImportResultDto
+{
+ public int RowsRead { get; set; }
+
+ public int ProvincesCreated { get; set; }
+ public int ProvincesUpdated { get; set; }
+
+ public int DistrictsCreated { get; set; }
+ public int DistrictsUpdated { get; set; }
+
+ public int SubdistrictsCreated { get; set; }
+ public int SubdistrictsUpdated { get; set; }
+}
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenCreateRequest.cs
new file mode 100644
index 0000000..ad5e55d
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+public sealed record AllergenCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenListRequest.cs
new file mode 100644
index 0000000..51487e4
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+public sealed record AllergenListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenResponse.cs
new file mode 100644
index 0000000..04615d2
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+public sealed record AllergenResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenUpdateRequest.cs
new file mode 100644
index 0000000..dc4946a
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Allergen/AllergenUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
+
+public sealed record AllergenUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandCreateRequest.cs
new file mode 100644
index 0000000..91060b8
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+public sealed record BrandCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandListRequest.cs
new file mode 100644
index 0000000..e3604fe
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+public sealed record BrandListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandResponse.cs
new file mode 100644
index 0000000..bba9e9f
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+public sealed record BrandResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandUpdateRequest.cs
new file mode 100644
index 0000000..a724ef5
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Brand/BrandUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
+
+public sealed record BrandUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryCreateRequest.cs
new file mode 100644
index 0000000..fb035b0
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Category;
+
+public sealed record CategoryCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryListRequest.cs
new file mode 100644
index 0000000..07fcd33
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Category;
+
+public sealed record CategoryListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryResponse.cs
new file mode 100644
index 0000000..454cc52
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Category;
+
+public sealed record CategoryResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryUpdateRequest.cs
new file mode 100644
index 0000000..93abd0d
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Category/CategoryUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Category;
+
+public sealed record CategoryUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusCreateRequest.cs
new file mode 100644
index 0000000..49dbbcd
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus;
+
+public sealed record ComplianceStatusCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusListRequest.cs
new file mode 100644
index 0000000..b5a3ac7
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus;
+
+public sealed record ComplianceStatusListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusResponse.cs
new file mode 100644
index 0000000..3943c40
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus;
+
+public sealed record ComplianceStatusResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusUpdateRequest.cs
new file mode 100644
index 0000000..5942070
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/ComplianceStatus/ComplianceStatusUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus;
+
+public sealed record ComplianceStatusUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryCreateRequest.cs
new file mode 100644
index 0000000..75d8397
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Country;
+
+public sealed record CountryCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryListRequest.cs
new file mode 100644
index 0000000..c2cb911
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Country;
+
+public sealed record CountryListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryResponse.cs
new file mode 100644
index 0000000..9bed6f1
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Country;
+
+public sealed record CountryResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryUpdateRequest.cs
new file mode 100644
index 0000000..aa46efe
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Country/CountryUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Country;
+
+public sealed record CountryUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyCreateRequest.cs
new file mode 100644
index 0000000..63c5366
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Currency;
+
+public sealed record CurrencyCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyListRequest.cs
new file mode 100644
index 0000000..708a957
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Currency;
+
+public sealed record CurrencyListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyResponse.cs
new file mode 100644
index 0000000..5dbfff4
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Currency;
+
+public sealed record CurrencyResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyUpdateRequest.cs
new file mode 100644
index 0000000..cca5fca
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Currency/CurrencyUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Currency;
+
+public sealed record CurrencyUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictListRequest.cs
new file mode 100644
index 0000000..00afc7c
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictListRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.District;
+
+public sealed record DistrictListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ Guid? ProvinceId = default,
+ string? ProvinceCode = default,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictResponse.cs
new file mode 100644
index 0000000..d248626
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/District/DistrictResponse.cs
@@ -0,0 +1,17 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.District;
+
+public sealed record DistrictResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta,
+ string DopaCode,
+ Guid ProvinceId,
+ string ProvinceName,
+ string ProvinceDopaCode
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusCreateRequest.cs
new file mode 100644
index 0000000..adc0dfa
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
+
+public sealed record DocControlStatusCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusListRequest.cs
new file mode 100644
index 0000000..a1a362c
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
+
+public sealed record DocControlStatusListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusResponse.cs
new file mode 100644
index 0000000..5246845
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
+
+public sealed record DocControlStatusResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusUpdateRequest.cs
new file mode 100644
index 0000000..c204c9f
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/DocControlStatus/DocControlStatusUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
+
+public sealed record DocControlStatusUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestCreateRequest.cs
new file mode 100644
index 0000000..fdabb39
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
+
+public sealed record FuncTestCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestListRequest.cs
new file mode 100644
index 0000000..a50e690
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
+
+public sealed record FuncTestListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestResponse.cs
new file mode 100644
index 0000000..34ad69c
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
+
+public sealed record FuncTestResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestUpdateRequest.cs
new file mode 100644
index 0000000..e9c029c
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/FuncTest/FuncTestUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
+
+public sealed record FuncTestUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassCreateRequest.cs
new file mode 100644
index 0000000..3d353be
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
+
+public sealed record HazardClassCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassListRequest.cs
new file mode 100644
index 0000000..184fc0e
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
+
+public sealed record HazardClassListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassResponse.cs
new file mode 100644
index 0000000..d154a2b
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
+
+public sealed record HazardClassResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassUpdateRequest.cs
new file mode 100644
index 0000000..dd8eb4e
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/HazardClass/HazardClassUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
+
+public sealed record HazardClassUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageCreateRequest.cs
new file mode 100644
index 0000000..40bfa8a
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Language;
+
+public sealed record LanguageCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageListRequest.cs
new file mode 100644
index 0000000..96af190
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Language;
+
+public sealed record LanguageListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageResponse.cs
new file mode 100644
index 0000000..209b164
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Language;
+
+public sealed record LanguageResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageUpdateRequest.cs
new file mode 100644
index 0000000..cf750f7
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Language/LanguageUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Language;
+
+public sealed record LanguageUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerCreateRequest.cs
new file mode 100644
index 0000000..2eea741
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
+
+public sealed record ManufacturerCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+ );
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerListRequest.cs
new file mode 100644
index 0000000..44183c9
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
+
+public sealed record ManufacturerListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerResponse.cs
new file mode 100644
index 0000000..a6e6ba5
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
+
+public sealed record ManufacturerResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerUpdateRequest.cs
new file mode 100644
index 0000000..fc60d68
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Manufacturer/ManufacturerUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
+
+public sealed record ManufacturerUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketCreateRequest.cs
new file mode 100644
index 0000000..beed05b
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Market;
+
+public sealed record MarketCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketListRequest.cs
new file mode 100644
index 0000000..a8f0af8
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Market;
+
+public sealed record MarketListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketResponse.cs
new file mode 100644
index 0000000..eda3ebc
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Market;
+
+public sealed record MarketResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketUpdateRequest.cs
new file mode 100644
index 0000000..9e3cb52
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Market/MarketUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Market;
+
+public sealed record MarketUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupCreateRequest.cs
new file mode 100644
index 0000000..a008599
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
+
+public sealed record PackingGroupCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupListRequest.cs
new file mode 100644
index 0000000..4505dda
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
+
+public sealed record PackingGroupListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupResponse.cs
new file mode 100644
index 0000000..4aeeffd
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
+
+public sealed record PackingGroupResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupUpdateRequest.cs
new file mode 100644
index 0000000..18ee57a
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/PackingGroup/PackingGroupUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
+
+public sealed record PackingGroupUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceListRequest.cs
new file mode 100644
index 0000000..2476c1e
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+
+public sealed record ProvinceListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceResponse.cs
new file mode 100644
index 0000000..fb93c34
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/Province/ProvinceResponse.cs
@@ -0,0 +1,14 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.Province;
+
+public sealed record ProvinceResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta,
+ string DopaCode
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageCreateRequest.cs
new file mode 100644
index 0000000..700e554
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
+
+public sealed record QaStageCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageListRequest.cs
new file mode 100644
index 0000000..1a2def9
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
+
+public sealed record QaStageListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageResponse.cs
new file mode 100644
index 0000000..cf7cc4d
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
+
+public sealed record QaStageResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageUpdateRequest.cs
new file mode 100644
index 0000000..841ef16
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QaStage/QaStageUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
+
+public sealed record QaStageUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusCreateRequest.cs
new file mode 100644
index 0000000..8c2ffcc
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
+
+public sealed record QcStatusCreateRequest(
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ string Scope = "tenant",
+ Guid? OverridesGlobalId = null,
+ bool IsActive = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusListRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusListRequest.cs
new file mode 100644
index 0000000..83f61d8
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusListRequest.cs
@@ -0,0 +1,9 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
+
+public sealed record QcStatusListRequest(
+ int Page = 1,
+ int PageSize = 20,
+ string? Search = null,
+ bool IncludeInactive = false,
+ bool IncludeOverrides = true
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusResponse.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusResponse.cs
new file mode 100644
index 0000000..534b2e0
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusResponse.cs
@@ -0,0 +1,13 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
+
+public sealed record QcStatusResponse(
+ Guid Id,
+ string Scope,
+ string Code,
+ string Name,
+ Dictionary? NameI18n,
+ Guid? OverridesGlobalId,
+ bool IsActive,
+ bool IsSystem,
+ Dictionary? Meta
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusUpdateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusUpdateRequest.cs
new file mode 100644
index 0000000..0d5e9f6
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/QcStatus/QcStatusUpdateRequest.cs
@@ -0,0 +1,8 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
+
+public sealed record QcStatusUpdateRequest(
+ string Name,
+ Dictionary? NameI18n,
+ Dictionary? Meta,
+ bool IsActive
+);
\ No newline at end of file
diff --git a/AMREZ.EOP.Contracts/DTOs/MasterData/RecallClass/RecallClassCreateRequest.cs b/AMREZ.EOP.Contracts/DTOs/MasterData/RecallClass/RecallClassCreateRequest.cs
new file mode 100644
index 0000000..500dcac
--- /dev/null
+++ b/AMREZ.EOP.Contracts/DTOs/MasterData/RecallClass/RecallClassCreateRequest.cs
@@ -0,0 +1,11 @@
+namespace AMREZ.EOP.Contracts.DTOs.MasterData.RecallClass;
+
+public sealed record RecallClassCreateRequest(
+ string Code,
+ string Name,
+ Dictionary