[CLAUDE] Domain+App+Infra+Api+FE-Admin+FE-User: S36 Plan G-O2 Phòng họp Mig 36 + BE CRUD + FE 2 app
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m55s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m55s
Phase 10.2 G-O2 Phòng họp BookingCalendar — Mig 36 schema + BE CQRS + FE 2 app mirror cookie-cutter G-H2 HrmConfig pattern. Standalone không depend workflow. BE schema (Mig 36 — em main solo Step 4): - 4 Domain new: MeetingRoom catalog + MeetingBooking header + MeetingBookingAttendee join table N-to-N (NOT JSON per Investigator verdict) + Enums (MeetingBookingStatus 3-state: Confirmed/Cancelled/Completed) - 3 EF Config: UNIQUE Code + composite index (RoomId, StartAt) range query + UNIQUE composite (BookingId, UserId) join - FK strategy: Room→Restrict (preserve history) + Booking→Cascade attendees + User→Restrict (denorm FullName+Email tránh cascade wipe) - Mig 36 3-file rule + ApplicationDbContextModelSnapshot updated + apply Dev+Design DB BE CQRS (~584 LOC — Implementer Case 2): - MeetingFeatures.cs 479 LOC 9 handler: 4 Room CRUD + 5 Booking (List + GetById + Create + Update + Cancel) - SERIALIZABLE transaction overlap check via EXISTS query — throw 409 Conflict "Phòng đã được đặt trong khoảng thời gian này" - MeetingRoomsController 49 LOC + MeetingBookingsController 56 LOC — class-level [Authorize] + Roles="Admin" for write - Application.csproj +Microsoft.EntityFrameworkCore.Relational package (em main fix IsolationLevel overload — Implementer gotcha #53 4th truncation diagnose mid-task) - MenuKeys.cs +4 const (Off_PhongHop sub-group + View/Manage/Book leaf) - DbInitializer +SeedMeetingRoomsAsync 4 sample (PH-A Phòng họp lớn cap=20 + PH-B cap=8 + PHG-501 Giám đốc cap=6 + ONL-1 Online Zoom cap=50) — NOT gated DemoSeed per gotcha #51 INFRASTRUCTURE seed FE 2 app (~1770 LOC × 2 — Implementer Case 2): - types/meeting.ts × 2 SHA256 IDENTICAL (ce0ad9c6d017cde2) — DTO interface mirror - MeetingCalendarPage.tsx × 2 SHA256 IDENTICAL (d6d160ae1e4f2285) ~530 LOC — custom HTML 7-day grid 8h-20h slot, NO FullCalendar dep (~80 KB bundle saved per Investigator verdict alternative) - MeetingRoomsPage.tsx × 2 SHA256 IDENTICAL (ba35a7ef379a5e9c) ~270 LOC — admin catalog CRUD table + Dialog - 4-place mirror Pattern 16-bis 7× cumulative: types + page + App.tsx route + menuKeys + Layout staticMap 3 entry (gotcha #50 silent sidebar drop prevention) Verify: - dotnet build SolutionErp.slnx PASS 0 error 2 pre-existing DocxRenderer warning - dotnet test 130/130 PASS baseline preserve (58 Domain + 72 Infra) - npm build × 2 app PASS 0 TS error (fe-admin 16.91s bundle 1490 KB / fe-user 8.56s bundle 1404 KB, +23 KB gzip both) Pattern reinforced cumulative S36: - Pattern 12-bis cross-module mirror 10× (PE → Contract V2 → Hrm → Office) - Pattern 16-bis 4-place mirror cross-app 7× - Smart Friend Implementer truncation gotcha #53 4th — mitigation tight brief WORK (FE 2 app no truncation, BE truncate diagnose mid only) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -99,6 +99,11 @@ public static class MenuKeys
|
||||
// ============================================================
|
||||
public const string Off = "Off"; // root group văn phòng số
|
||||
public const string OffDanhBa = "Off_DanhBa"; // Danh bạ nội bộ (card grid)
|
||||
// Phase 10.2 G-O2 (Mig 36 — S36 2026-05-28) — Phòng họp + Booking calendar.
|
||||
public const string OffPhongHop = "Off_PhongHop"; // sub-group phòng họp
|
||||
public const string OffPhongHopView = "Off_PhongHop_View"; // Xem lịch (FullCalendar)
|
||||
public const string OffPhongHopManage = "Off_PhongHop_Manage"; // Quản lý phòng (Admin CRUD MeetingRoom)
|
||||
public const string OffPhongHopBook = "Off_PhongHop_Book"; // Đặt phòng (Create/Update/Cancel Booking)
|
||||
|
||||
public static readonly string[] PurchaseEvaluationTypeCodes =
|
||||
["DuyetNcc", "DuyetNccPhuongAn"];
|
||||
@ -127,6 +132,7 @@ public static class MenuKeys
|
||||
Hrm, HrmHoSo, // Mig 34 — Phase 10.1
|
||||
HrmConfig, HrmConfigLeaveTypes, HrmConfigHolidays, HrmConfigShifts, HrmConfigOtPolicies, // Mig 35 — Phase 10.2 G-H2
|
||||
Off, OffDanhBa, // Phase 10.2 G-O1 — Văn phòng số
|
||||
OffPhongHop, OffPhongHopView, OffPhongHopManage, OffPhongHopBook, // Phase 10.2 G-O2 — Phòng họp
|
||||
System, Users, Roles, Permissions, MenuVisibility, Workflows, PeWorkflows,
|
||||
ApprovalWorkflowsV2, ApprovalWorkflowDuyetNccV2, ApprovalWorkflowDuyetNccPhuongAnV2, // Mig 22
|
||||
];
|
||||
|
||||
12
src/Backend/SolutionErp.Domain/Office/Enums.cs
Normal file
12
src/Backend/SolutionErp.Domain/Office/Enums.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.2 G-O2 — Phòng họp BookingCalendar enum.
|
||||
// Reference NamGroup TblBookingResource TrangThai 4-state (NamGroup S50)
|
||||
// — SOL adapt 3-state, drop "Rejected" (use Cancelled instead).
|
||||
|
||||
public enum MeetingBookingStatus
|
||||
{
|
||||
Confirmed = 1, // Đã xác nhận (default sau Create)
|
||||
Cancelled = 2, // Đã huỷ
|
||||
Completed = 3, // Đã kết thúc (auto-set khi EndAt < Now via job/manual)
|
||||
}
|
||||
29
src/Backend/SolutionErp.Domain/Office/MeetingBooking.cs
Normal file
29
src/Backend/SolutionErp.Domain/Office/MeetingBooking.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.2 G-O2 (Mig 36 — S36) — Booking phòng họp.
|
||||
// Overlap check qua SERIALIZABLE tx + EXISTS query trong handler
|
||||
// (NOT DB constraint vì datetime range not native SQL Server).
|
||||
// Reference NamGroup TblBookingResource S50 — SOL adapt 3-state status.
|
||||
public class MeetingBooking : AuditableEntity
|
||||
{
|
||||
public Guid RoomId { get; set; }
|
||||
public MeetingRoom Room { get; set; } = null!; // FK Restrict — không xoá Room còn booking active
|
||||
|
||||
public Guid BookedByUserId { get; set; } // User đặt phòng (Creator semantics)
|
||||
public string BookedByFullName { get; set; } = string.Empty; // denorm tránh cascade
|
||||
|
||||
public DateTime StartAt { get; set; } // UTC datetime2
|
||||
public DateTime EndAt { get; set; } // UTC datetime2. EndAt > StartAt (validator)
|
||||
|
||||
public string Title { get; set; } = string.Empty; // "Họp giao ban tuần", "Phỏng vấn ứng viên"
|
||||
public string? Description { get; set; } // Nội dung chi tiết / agenda
|
||||
|
||||
public MeetingBookingStatus Status { get; set; } = MeetingBookingStatus.Confirmed;
|
||||
|
||||
public string? Note { get; set; } // Ghi chú thêm (vd "Cần chuẩn bị nước uống")
|
||||
|
||||
// Attendees nav — collection join entity (NOT JSON, theo Investigator verdict S36)
|
||||
public List<MeetingBookingAttendee> Attendees { get; set; } = new();
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.2 G-O2 (Mig 36 — S36) — Attendee join table N-to-N.
|
||||
// Composite PK (BookingId, UserId) — không support add user 2 lần cùng booking.
|
||||
// FK Cascade Booking (xoá Booking → wipe Attendees) + Restrict User
|
||||
// (admin không xoá user còn invite — denorm name preserve).
|
||||
// Reference Investigator verdict: prefer join table over JSON array
|
||||
// để index query Notification push + reuse Users API multi-select FE.
|
||||
public class MeetingBookingAttendee : BaseEntity // NO IsDeleted — composite PK delete entire row
|
||||
{
|
||||
public Guid BookingId { get; set; }
|
||||
public MeetingBooking Booking { get; set; } = null!;
|
||||
|
||||
public Guid UserId { get; set; }
|
||||
public string FullName { get; set; } = string.Empty; // denorm tránh JOIN cho Notification push
|
||||
public string? Email { get; set; } // denorm cho email outbox future
|
||||
|
||||
public string? Notes { get; set; } // "Vắng buổi sáng", "Tham gia online"
|
||||
}
|
||||
23
src/Backend/SolutionErp.Domain/Office/MeetingRoom.cs
Normal file
23
src/Backend/SolutionErp.Domain/Office/MeetingRoom.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using SolutionErp.Domain.Common;
|
||||
|
||||
namespace SolutionErp.Domain.Office;
|
||||
|
||||
// Phase 10.2 G-O2 (Mig 36 — S36 2026-05-28) — Phòng họp catalog.
|
||||
// Reference NamGroup TblResource 11 cols (S50 design) — SOL adapt clean-room
|
||||
// 3 entity: MeetingRoom (catalog) + MeetingBooking (header) + MeetingBookingAttendee (join).
|
||||
// AuditableEntity inherit chuẩn SOL Clean Arch.
|
||||
public class MeetingRoom : AuditableEntity
|
||||
{
|
||||
public string Code { get; set; } = string.Empty; // UNIQUE — "PH-A", "PH-B", "PHG-501"
|
||||
public string Name { get; set; } = string.Empty; // "Phòng họp tầng 5", "Phòng họp lớn"
|
||||
|
||||
// Sức chứa tối đa (số người). 0 = không giới hạn (mở rộng tự do).
|
||||
public int Capacity { get; set; }
|
||||
|
||||
public string? Location { get; set; } // "Tầng 5, Toà A" / "Trực tuyến Zoom"
|
||||
|
||||
// Trang thiết bị có sẵn (free text): "Máy chiếu, Bảng trắng, Wifi" — info-only.
|
||||
public string? Equipment { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true; // Soft-disable không xoá (preserve booking history)
|
||||
}
|
||||
Reference in New Issue
Block a user