# Thiết kế Entity - Quản lý Mặt bằng Bưu cục (Premise Management)

> Bản thiết kế cuối cùng, tổng hợp từ 2 bản thiết kế gốc (`premise-entities.md` & `ThietKeDuLieu_MatBang.md`),
> đã điều chỉnh phù hợp với cấu trúc thực thể hiện tại của hệ thống Ladaer.

---

## 1. Tổng quan

Module **Quản lý mặt bằng bưu cục** cho phép nhân viên khảo sát, đánh giá và quản lý các mặt bằng tiềm năng để mở bưu cục mới. Mỗi mặt bằng được liên kết với 1 bưu cục (`Agency`) hiện tại.

### Nguyên tắc thiết kế

| Nguyên tắc | Mô tả |
|---|---|
| **Aggregate Root** | `Premise` là thực thể trung tâm, mọi thao tác đi qua entity này |
| **Kế thừa framework** | Sử dụng `BaseEntityWithKey<int>` (ID kiểu `int`, audit fields từ `BaseEntity`) |
| **Tái sử dụng** | Tái sử dụng `Location`, `Media`, `Agency` có sẵn trong hệ thống |
| **Chuẩn hóa (Normalize)** | Tách tài chính/pháp lý (`PremiseFinancial`) khỏi thông số vật lý (`Premise`) |
| **Nhiều liên hệ** | Hỗ trợ nhiều người liên hệ (chủ nhà, môi giới, người được ủy quyền) |
| **Chấm điểm động** | Hệ thống tiêu chí chấm điểm cấu hình được (thay thế enum cố định) |
| **Media linh hoạt** | Bảng trung gian `PremiseMedia` liên kết với `Media` có sẵn, phân loại ảnh/video/tài liệu |

---

## 2. Sơ đồ quan hệ

```
Agency (1) ──────── (n) Premise                  ← Mỗi mặt bằng liên kết 1 bưu cục
Location (1) ─────── (n) Premise                  ← Vị trí phường/xã
Premise (1) ──────── (1) PremiseFinancial         ← Thông tin tài chính & pháp lý
Premise (1) ──────── (n) PremiseContact           ← Liên hệ (chủ nhà, môi giới)
Premise (1) ──────── (n) PremiseScore             ← Điểm chấm theo từng tiêu chí
Premise (1) ──────── (n) PremiseMedia             ← Hình ảnh, video, tài liệu
Media (1) ────────── (n) PremiseMedia             ← Tái sử dụng bảng Media có sẵn

PremiseScoringCriteria (1) ── (n) PremiseScoringOption  ← Cấu hình tiêu chí & điểm
PremiseScoringCriteria (1) ── (n) PremiseScore          ← Điểm chấm của mặt bằng
PremiseScoringOption (1) ──── (n) PremiseScore          ← Option được chọn
```

---

## 3. Chuỗi kế thừa hiện tại (tham khảo)

```
BaseEntity                          ← CreatedTime, UpdatedTime, IsDeleted, CreatedBy (int), ...
  └── BaseEntityWithKey<TKey>       ← Id (TKey)
        └── BaseCategory            ← Name, Description, DisplayOrder (dùng cho Location, Agency)
```

> **Lưu ý:** `Premise` và các entity con sẽ kế thừa `BaseEntityWithKey<int>`, **không** kế thừa `BaseCategory` vì Premise không phải danh mục đơn giản.

---

## 4. Enums

```csharp
namespace Lada.Framework.Data.Enums
{
    /// <summary>
    /// Trạng thái quy trình mặt bằng
    /// </summary>
    public enum PremiseStatus
    {
        Surveying    = 1,  // Đang khảo sát
        Evaluating   = 2,  // Đang đánh giá
        Negotiating  = 3,  // Đang đàm phán
        Approved     = 4,  // Đã duyệt
        Rented       = 5,  // Đã thuê
        Rejected     = 6,  // Từ chối
        Closed       = 7   // Đã đóng
    }

    /// <summary>
    /// Loại tiêu chí chấm điểm
    /// </summary>
    public enum ScoringCriteriaType
    {
        Option = 1,  // Chọn từ danh sách (VD: Loại mặt bằng, Hướng đường)
        Range  = 2   // Khoảng giá trị (VD: Giá thuê, Diện tích)
    }

    /// <summary>
    /// Vai trò liên hệ mặt bằng
    /// </summary>
    public enum PremiseContactRole
    {
        Landlord         = 1,  // Chủ nhà
        Broker           = 2,  // Môi giới
        AuthorizedPerson = 3   // Người được ủy quyền
    }

    /// <summary>
    /// Phân loại media mặt bằng
    /// </summary>
    public enum PremiseMediaType
    {
        FrontageImage = 1,  // Ảnh mặt tiền
        InsideImage   = 2,  // Ảnh bên trong
        StreetImage   = 3,  // Ảnh đường/vỉa hè
        Video         = 4,  // Video toàn cảnh
        LegalDocument = 5   // Giấy tờ pháp lý
    }
}
```

> **Thay đổi so với phiên bản trước:** Đã bỏ các enum cố định `PremiseType`, `PremiseLocationType`, `RoadDirection`. Những thuộc tính này chuyển sang hệ thống tiêu chí động `PremiseScoringCriteria` + `PremiseScoringOption` để cấu hình linh hoạt và gắn liền với điểm chấm.

---

## 5. Định nghĩa Entity

### 5.1 Premise (Aggregate Root)

Thực thể trung tâm: thông tin vật lý, vị trí, giao thông & môi trường xung quanh.

```csharp
using System.ComponentModel.DataAnnotations;
using Lada.Framework.Data.Domains.BaseEntities;

namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Mặt bằng bưu cục - Aggregate Root
    /// Thông số đặc tả (được chấm điểm) được lưu trong PremiseScore
    /// </summary>
    public class Premise : BaseEntityWithKey<int>
    {
        /// <summary>
        /// Mã mặt bằng (tự sinh hoặc nhập tay)
        /// </summary>
        [MaxLength(50)]
        public string Code { get; set; } = string.Empty;

        /// <summary>
        /// Trạng thái quy trình
        /// </summary>
        public PremiseStatus Status { get; set; } = PremiseStatus.Surveying;

        #region Liên kết hệ thống

        /// <summary>
        /// Bưu cục liên kết (FK → Agency)
        /// </summary>
        public int AgencyId { get; set; }

        /// <summary>
        /// Vị trí phường/xã (FK → Location)
        /// </summary>
        public int LocationId { get; set; }

        #endregion

        #region Định vị chi tiết

        /// <summary>
        /// Địa chỉ chi tiết (số nhà, tên đường)
        /// </summary>
        [MaxLength(500)]
        public string AddressDetail { get; set; } = string.Empty;

        /// <summary>
        /// Vĩ độ
        /// </summary>
        public double? Latitude { get; set; }

        /// <summary>
        /// Kinh độ
        /// </summary>
        public double? Longitude { get; set; }

        /// <summary>
        /// Link Google Maps
        /// </summary>
        [MaxLength(1000)]
        public string? GoogleMapUrl { get; set; }

        #endregion

        #region Tổng điểm

        /// <summary>
        /// Tổng điểm mặt bằng (tính từ tổng PremiseScore)
        /// Được tính lại khi có thay đổi điểm
        /// </summary>
        public int TotalScore { get; set; }

        #endregion

        #region Ghi chú chung

        /// <summary>
        /// Mô tả / ghi chú tổng quan
        /// </summary>
        [MaxLength(4000)]
        public string? Description { get; set; }

        #endregion

        #region Navigation Properties

        public virtual Agency Agency { get; set; } = null!;
        public virtual Location Location { get; set; } = null!;

        public virtual PremiseFinancial? Financial { get; set; }
        public virtual ICollection<PremiseContact> Contacts { get; set; } = new List<PremiseContact>();
        public virtual ICollection<PremiseScore> Scores { get; set; } = new List<PremiseScore>();
        public virtual ICollection<PremiseMedia> PremiseMedias { get; set; } = new List<PremiseMedia>();

        #endregion
    }
}
```

> **Thay đổi:** Bỏ toàn bộ các field enum cố định (`PremiseType`, `LocationType`, `RoadDirection`, `HasTimeRestriction`, `CanAccommodateLightTrucks`...) và các field số liệu vật lý (`FrontageWidthM`, `AreaM2`, `SidewalkWidthM`...). Tất cả chuyển sang hệ thống chấm điểm động `PremiseScore` với giá trị text và điểm cấu hình được. Thêm `TotalScore` để tính tổng điểm.

---

### 5.2 PremiseFinancial (1–1 với Premise)

Tách thông tin tài chính, hợp đồng & pháp lý khỏi thông số vật lý để dễ phân quyền.

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Thông tin tài chính, hợp đồng và pháp lý của mặt bằng
    /// </summary>
    public class PremiseFinancial : BaseEntityWithKey<int>
    {
        /// <summary>
        /// FK → Premise
        /// </summary>
        public int PremiseId { get; set; }

        /// <summary>
        /// Giá chào thuê / tháng (VNĐ)
        /// </summary>
        public decimal? AskingRent { get; set; }

        /// <summary>
        /// Giá chốt / tháng (VNĐ)
        /// </summary>
        public decimal? NegotiatedRent { get; set; }

        /// <summary>
        /// Số tháng đặt cọc
        /// </summary>
        public int? DepositMonths { get; set; }

        /// <summary>
        /// Kỳ thanh toán (1, 3, 6 tháng...)
        /// </summary>
        public int? PaymentPeriodMonths { get; set; }

        /// <summary>
        /// Số ngày setup miễn phí tối đa
        /// </summary>
        public int? FreeFitOutDays { get; set; }

        /// <summary>
        /// Ngày có thể nhận mặt bằng
        /// </summary>
        public DateTime? AvailableFromDate { get; set; }

        /// <summary>
        /// Có xuất được hóa đơn VAT?
        /// </summary>
        public bool CanProvideVatInvoice { get; set; }

        /// <summary>
        /// Ghi chú hợp đồng / pháp lý
        /// </summary>
        [MaxLength(4000)]
        public string? LegalNote { get; set; }

        #region Navigation Properties

        public virtual Premise Premise { get; set; } = null!;

        #endregion
    }
}
```

---

### 5.3 PremiseContact (n liên hệ cho mỗi mặt bằng)

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Liên hệ mặt bằng (chủ nhà, môi giới, người ủy quyền)
    /// </summary>
    public class PremiseContact : BaseEntityWithKey<int>
    {
        /// <summary>
        /// FK → Premise
        /// </summary>
        public int PremiseId { get; set; }

        /// <summary>
        /// Họ tên người liên hệ
        /// </summary>
        [MaxLength(200)]
        public string FullName { get; set; } = string.Empty;

        /// <summary>
        /// Số điện thoại
        /// </summary>
        [MaxLength(20)]
        public string PhoneNumber { get; set; } = string.Empty;

        /// <summary>
        /// Vai trò (Chủ nhà / Môi giới / Người ủy quyền)
        /// </summary>
        public PremiseContactRole Role { get; set; } = PremiseContactRole.Landlord;

        /// <summary>
        /// Ghi chú
        /// </summary>
        [MaxLength(500)]
        public string? Note { get; set; }

        #region Navigation Properties

        public virtual Premise Premise { get; set; } = null!;

        #endregion
    }
}
```

---

### 5.4 PremiseScoringCriteria (Cấu hình tiêu chí chấm điểm)

Bảng **cấu hình** do admin quản lý. Mỗi tiêu chí có loại: **Option** (chọn từ danh sách) hoặc **Range** (khoảng giá trị số).

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Tiêu chí chấm điểm mặt bằng (admin cấu hình)
    /// VD: "Loại mặt bằng", "Vị trí", "Hướng đường", "Giá thuê/tháng"
    /// </summary>
    public class PremiseScoringCriteria : BaseEntityWithKey<int>
    {
        /// <summary>
        /// Tên tiêu chí
        /// </summary>
        [MaxLength(200)]
        public string Name { get; set; } = string.Empty;

        /// <summary>
        /// Mô tả / hướng dẫn nhập
        /// </summary>
        [MaxLength(500)]
        public string? Description { get; set; }

        /// <summary>
        /// Loại tiêu chí:
        /// Option = chọn từ danh sách (VD: Nhà thô, Kho tôn...)
        /// Range = khoảng giá trị (VD: giá thuê < 5tr = 10 điểm)
        /// </summary>
        public ScoringCriteriaType CriteriaType { get; set; }

        /// <summary>
        /// Thứ tự hiển thị
        /// </summary>
        public int DisplayOrder { get; set; }

        /// <summary>
        /// Còn sử dụng?
        /// </summary>
        public bool IsActive { get; set; } = true;

        #region Navigation Properties

        public virtual ICollection<PremiseScoringOption> Options { get; set; } = new List<PremiseScoringOption>();

        #endregion
    }
}
```

---

### 5.5 PremiseScoringOption (Giá trị + Điểm cho mỗi tiêu chí)

Mỗi tiêu chí có nhiều option. Điểm được cấu hình sẵn bởi admin.

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Option/khoảng giá trị kèm điểm cho mỗi tiêu chí
    /// VD: "Nhà thô" = 5 điểm, "Giá < 5 triệu" = 10 điểm
    /// </summary>
    public class PremiseScoringOption : BaseEntityWithKey<int>
    {
        /// <summary>
        /// FK → PremiseScoringCriteria
        /// </summary>
        public int CriteriaId { get; set; }

        /// <summary>
        /// Nhãn hiển thị (VD: "Nhà thô", "< 5 triệu", "1 chiều")
        /// </summary>
        [MaxLength(200)]
        public string Label { get; set; } = string.Empty;

        /// <summary>
        /// Giá trị min (chỉ dùng cho CriteriaType = Range)
        /// null = không giới hạn dưới
        /// </summary>
        public decimal? MinValue { get; set; }

        /// <summary>
        /// Giá trị max (chỉ dùng cho CriteriaType = Range)
        /// null = không giới hạn trên
        /// </summary>
        public decimal? MaxValue { get; set; }

        /// <summary>
        /// Điểm khi chọn option này
        /// </summary>
        public int Score { get; set; }

        /// <summary>
        /// Thứ tự hiển thị
        /// </summary>
        public int DisplayOrder { get; set; }

        #region Navigation Properties

        public virtual PremiseScoringCriteria Criteria { get; set; } = null!;

        #endregion
    }
}
```

---

### 5.6 PremiseScore (Điểm chấm của từng mặt bằng)

Lưu giá trị NV khảo sát nhập + điểm tương ứng cho mỗi tiêu chí.

> **Cách xác định vai trò:**
> - **NV khảo sát** (người tạo mặt bằng) nhập thông tin và chấm điểm
> - **Quản lý** (xác định qua `ManagerId` của User) xem xét tổng điểm để phê duyệt
> - Cấu hình qua `AppConfig`

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Điểm chấm của 1 mặt bằng theo từng tiêu chí
    /// </summary>
    public class PremiseScore : BaseEntityWithKey<int>
    {
        /// <summary>
        /// FK → Premise
        /// </summary>
        public int PremiseId { get; set; }

        /// <summary>
        /// FK → PremiseScoringCriteria
        /// </summary>
        public int CriteriaId { get; set; }

        /// <summary>
        /// FK → PremiseScoringOption (option được chọn, nullable cho nhập tay)
        /// </summary>
        public int? SelectedOptionId { get; set; }

        /// <summary>
        /// Giá trị text do NV nhập (VD: "12", "Nhà thô 2 tầng")
        /// </summary>
        [MaxLength(500)]
        public string? TextValue { get; set; }

        /// <summary>
        /// Giá trị số (dùng để tự khớp khoảng Range, VD: 8000000 → khớp "5-8 triệu")
        /// </summary>
        public decimal? NumericValue { get; set; }

        /// <summary>
        /// Điểm chấm (auto từ Option/Range hoặc override thủ công)
        /// </summary>
        public int Score { get; set; }

        /// <summary>
        /// Ghi chú của NV khảo sát
        /// </summary>
        [MaxLength(1000)]
        public string? Note { get; set; }

        #region Navigation Properties

        public virtual Premise Premise { get; set; } = null!;
        public virtual PremiseScoringCriteria Criteria { get; set; } = null!;
        public virtual PremiseScoringOption? SelectedOption { get; set; }

        #endregion
    }
}
```

---

### 5.6.1 Ví dụ cấu hình tiêu chí (Seed Data)

| Tiêu chí | Loại | Option | Điểm |
|-----------|------|--------|------|
| **Loại mặt bằng** | Option | Nhà thô | 5 |
| | | Nhà hoàn thiện | 8 |
| | | Kho tôn | 3 |
| | | Kho bê tông | 6 |
| **Vị trí** | Option | Mặt phố | 10 |
| | | Mặt ngõ/hẻm | 5 |
| | | Khu công nghiệp | 3 |
| **Hướng đường** | Option | 2 chiều | 8 |
| | | 1 chiều | 4 |
| **Xe tải lùi/quay đầu** | Option | Có thể | 10 |
| | | Không thể | 2 |
| **Cấm giờ / cấm tải** | Option | Không cấm | 10 |
| | | Có cấm | 3 |
| **Giá thuê/tháng** | Range | < 5 triệu | 10 |
| | | 5 – 8 triệu | 7 |
| | | 8 – 10 triệu | 6 |
| | | 10 – 12 triệu | 5 |
| | | 12 – 15 triệu | 3 |
| | | 15 – 20 triệu | 1 |
| **Diện tích (m²)** | Range | > 100 | 10 |
| | | 70 – 100 | 8 |
| | | 50 – 70 | 6 |
| | | < 50 | 3 |
| **Mặt tiền (m)** | Range | > 8 | 10 |
| | | 5 – 8 | 7 |
| | | 3 – 5 | 4 |
| | | < 3 | 2 |

---

### 5.7 PremiseMedia (bảng trung gian Premise ↔ Media)

Theo pattern giống `TruckMedia` hiện có trong hệ thống.

```csharp
namespace Lada.Framework.Data.Domains.Premises
{
    /// <summary>
    /// Bảng trung gian liên kết Mặt bằng với Media có sẵn
    /// </summary>
    public class PremiseMedia : BaseEntityWithKey<int>
    {
        /// <summary>
        /// FK → Premise
        /// </summary>
        public int PremiseId { get; set; }

        /// <summary>
        /// FK → Media (Guid)
        /// </summary>
        public Guid MediaId { get; set; }

        /// <summary>
        /// Phân loại media (ảnh mặt tiền, bên trong, đường, tài liệu...)
        /// </summary>
        public PremiseMediaType MediaType { get; set; }

        /// <summary>
        /// Thứ tự hiển thị
        /// </summary>
        public int DisplayOrder { get; set; }

        #region Navigation Properties

        public virtual Premise Premise { get; set; } = null!;
        public virtual Media Media { get; set; } = null!;

        #endregion
    }
}
```

---

## 6. Luồng công việc (Workflow)

### 6.1 Sơ đồ trạng thái

```mermaid
stateDiagram-v2
    [*] --> Surveying : NV khảo sát tạo mặt bằng

    Surveying --> Evaluating : Hoàn tất khảo sát,\nchấm điểm tiêu chí
    Surveying --> Closed : Hủy bỏ

    Evaluating --> Negotiating : QL duyệt đánh giá,\nchuyển đàm phán
    Evaluating --> Rejected : QL từ chối
    Evaluating --> Surveying : QL yêu cầu\nkhảo sát lại

    Negotiating --> Approved : Đàm phán thành công,\nQL phê duyệt
    Negotiating --> Rejected : Đàm phán thất bại
    Negotiating --> Evaluating : Cần đánh giá lại

    Approved --> Rented : Ký hợp đồng,\nbắt đầu thuê
    Approved --> Closed : Hủy phê duyệt

    Rejected --> Surveying : Mở lại khảo sát
    Rejected --> Closed : Đóng vĩnh viễn

    Rented --> Closed : Hết hợp đồng /\nchấm dứt thuê

    Closed --> [*]
```

### 6.2 Mô tả luồng chi tiết

| Bước | Trạng thái | Ai thực hiện | Hành động |
|------|-----------|-------------|----------|
| 1 | **Surveying** | NV khảo sát (người tạo) | Tạo mặt bằng, nhập thông tin cơ bản, liên hệ, chụp ảnh. **Chấm điểm từng tiêu chí** (chọn option hoặc nhập giá trị) |
| 2 | **Evaluating** | NV → Quản lý | NV hoàn tất chấm điểm → QL xem xét tổng điểm, quyết định |
| 3 | **Negotiating** | NV khảo sát | Đàm phán giá thuê, điều khoản hợp đồng. Nhập thông tin tài chính |
| 4 | **Approved** | Quản lý | QL phê duyệt điều khoản cuối cùng |
| 5 | **Rented** | Hệ thống | Ký hợp đồng, bắt đầu thuê |
| 6 | **Rejected** | Quản lý | Từ chối tại bất kỳ giai đoạn nào |
| 7 | **Closed** | Hệ thống / QL | Hết hợp đồng hoặc đóng vĩnh viễn |

---

## 7. Luồng dữ liệu (Data Flow)

### 7.1 Giai đoạn Khảo sát (Surveying)

```
NV khảo sát
    │
    ├── Tạo Premise ─────────────── → Premise (Code, Status=Surveying, AgencyId, LocationId,
    │                                          AddressDetail, Latitude, Longitude)
    │                                 BaseEntity.CreatedBy = UserId nhân viên
    │
    ├── Chấm điểm tiêu chí ──────── → PremiseScore[] (cho mỗi criteria:
    │                                    CriteriaId, SelectedOptionId hoặc NumericValue,
    │                                    TextValue, Score)
    │                                 → Premise.TotalScore = SUM(PremiseScore.Score)
    │
    ├── Thêm liên hệ ─────────────  → PremiseContact[] (FullName, PhoneNumber, Role)
    │
    └── Upload ảnh/video ──────────  → Media → PremiseMedia (MediaType, DisplayOrder)
```

### 7.2 Giai đoạn Đánh giá (Evaluating)

```
NV khảo sát (người tạo)                Quản lý (User.ManagerId)
    │                                       │
    ├── Hoàn tất chấm điểm ────── →        │
    │   Chuyển Status = Evaluating          │
    │                                       │
    │                                       ├── Xem tổng điểm & chi tiết
    │                                       │   (Premise.TotalScore + PremiseScore[])
    │                                       │
    │                                       └── Quyết định:
    │                                           ├── Duyệt → Status = Negotiating
    │                                           ├── Từ chối → Status = Rejected
    │                                           └── Khảo sát lại → Status = Surveying
```

### 7.3 Giai đoạn Đàm phán (Negotiating)

```
NV khảo sát
    │
    ├── Nhập thông tin tài chính ── → PremiseFinancial
    │                                  (AskingRent, NegotiatedRent, DepositMonths,
    │                                   PaymentPeriodMonths, FreeFitOutDays,
    │                                   AvailableFromDate, CanProvideVatInvoice, LegalNote)
    │
    ├── Cập nhật điểm giá thuê ──── → PremiseScore (criteria "Giá thuê/tháng",
    │                                   NumericValue → auto match Range → Score)
    │                                 → Premise.TotalScore tính lại
    │
    ├── Cập nhật liên hệ ───────── → PremiseContact[] (thêm/sửa)
    │
    └── Upload tài liệu pháp lý ── → PremiseMedia (MediaType = LegalDocument)
```

### 7.4 Giai đoạn Phê duyệt → Thuê (Approved → Rented)

```
Quản lý
    │
    ├── Phê duyệt ──────────────── → Premise.Status = Approved
    │
    └── Xác nhận thuê ──────────── → Premise.Status = Rented
```

### 7.5 Tổng hợp: Entity nào được ghi ở từng giai đoạn

| Giai đoạn | Premise | PremiseContact | PremiseScore | PremiseMedia | PremiseFinancial |
|-----------|:-------:|:--------------:|:------------:|:------------:|:----------------:|
| Surveying | ✏️ Tạo/Sửa | ✏️ Tạo/Sửa | ✏️ Tạo | ✏️ Tạo | — |
| Evaluating | ✏️ Status | — | — | — | — |
| Negotiating | ✏️ Status | ✏️ Sửa | ✏️ Cập nhật (giá) | ✏️ Tạo (tài liệu) | ✏️ Tạo/Sửa |
| Approved | ✏️ Status | — | — | — | — |
| Rented | ✏️ Status | — | — | — | — |
| Closed | ✏️ Status | — | — | — | — |

---

## 8. So sánh với 2 bản thiết kế gốc

| Khía cạnh | `premise-entities.md` | `ThietKeDuLieu_MatBang.md` | **Bản cuối (này)** |
|---|---|---|---|
| **Kiểu ID** | `Guid` | `int` | `int` ✅ (theo framework) |
| **Base class** | Không kế thừa | Không kế thừa | `BaseEntityWithKey<int>` ✅ |
| **Audit fields** | Tự định nghĩa (`CreatedAt`, `CreatedByUserId`) | Không có | Kế thừa từ `BaseEntity` ✅ |
| **Soft delete** | Không có | Không có | Kế thừa `IsDeleted` từ `BaseEntity` ✅ |
| **Liên kết Agency** | Không có | Không có | Thêm `AgencyId` ✅ |
| **Tài chính** | `PremiseRentalTerm` (n bản ghi lịch sử) | `PremiseFinancial` (1-1) | `PremiseFinancial` (1-1) ✅ |
| **Pháp lý** | `PremiseLegalInfo` (entity riêng) | Gộp vào `PremiseFinancial` | Gộp vào `PremiseFinancial` ✅ |
| **Liên hệ** | `PremiseContact` | `Contact` | `PremiseContact` ✅ (đặt tên rõ hơn) |
| **Chấm điểm** | `PremiseReview` (thủ công) | `Evaluation` (thủ công) | `PremiseScoringCriteria` + `PremiseScore` ✅ (động, cấu hình được) |
| **Media** | `PremiseMedia` (có `Note`) | `PremiseMedia` | `PremiseMedia` ✅ (theo pattern `TruckMedia`) |
| **LocationType** | Dùng enum vật lý | Enum riêng | `PremiseLocationType` ✅ |
| **RentalTerm lịch sử** | Có (nhiều bản ghi) | Không | Không (giữ đơn giản, có thể mở rộng sau) |

---

## 9. Ghi chú triển khai

### 9.1 Namespace & Thư mục

Tất cả entity đặt trong:
- **Namespace**: `Lada.Framework.Data.Domains.Premises`
- **Thư mục**: `Lada.Framework/Data/Domains/Premises/`

Enums đặt trong:
- **File**: `Lada.Framework/Data/Enums/PremiseEnums.cs`

### 9.2 Fluent API (EntityMapping)

Tạo mapping theo pattern hiện có tại `Lada.Framework/Data/Mappings/EntityMappings/`:
- Unique index trên `PremiseFinancial.PremiseId` (quan hệ 1-1)
- Unique index trên `PremiseScore` (`PremiseId` + `CriteriaId`) — mỗi mặt bằng chỉ 1 điểm/tiêu chí
- Index trên `Premise.AgencyId`, `Premise.LocationId`, `Premise.Status`, `Premise.TotalScore`
- Cascade delete từ `Premise` → `PremiseScore`, `PremiseContact`, `PremiseMedia`, `PremiseFinancial`
- Cascade delete từ `PremiseScoringCriteria` → `PremiseScoringOption`

### 9.3 DbContext

Thêm các `DbSet` vào `LadaDbContext`:
```csharp
public DbSet<Premise> Premises { get; set; }
public DbSet<PremiseFinancial> PremiseFinancials { get; set; }
public DbSet<PremiseContact> PremiseContacts { get; set; }
public DbSet<PremiseScoringCriteria> PremiseScoringCriteria { get; set; }
public DbSet<PremiseScoringOption> PremiseScoringOptions { get; set; }
public DbSet<PremiseScore> PremiseScores { get; set; }
public DbSet<PremiseMedia> PremiseMedias { get; set; }
```

### 9.4 Migration

```bash
dotnet ef migrations add AddPremiseEntities -p Ladaer.BackEnd\Ladaer.BackEnd.csproj
dotnet ef database update -p Ladaer.BackEnd\Ladaer.BackEnd.csproj
```
