[CLAUDE] Supplier: Mig 62 expand 30-field + seed 4 NCC + FE form 2-app
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m7s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m7s
Phase A of the NCC/TP Excel-import backlog (anh Kiet FDC). Supplier entity 9 -> 27 fields to hold the 30-column supplier database. Backend: - Supplier +18 nullable cols; SupplierType +CaHai=6 (append-only); new nullable SupplierStatus enum. - Mig 62 ExpandSupplierFields (3-file): AddColumn x18 + guarded un-cram Sql (clear legacy crammed Note on the 4 seeded rows, exact-match). No new table (89), reversible Down. - Seed fill-nulls-if-exists: populate 30 fields for TRUONGGIANG/TANPHU/ TGN/DONGDUONG from Excel; idempotent, never clobbers existing cols. - DTO + Create/Update commands + validators (MaximumLength-only, no NotEmpty so open-auth Create minimal payload stays valid) + 2 query projections. Frontend (fe-admin + fe-user, byte-identical SHA-mirror): - types/master.ts +18 fields +SupplierStatus; SuppliersPage form 4 section groups + Status select + 3 NAS links via new shared PathLink (extracted from PE HoSoLink render+Copy). All new fields optional. Excel upload auto-map = Phase B (deferred). Tests test-after (UAT mode). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -16,7 +16,27 @@ public record CreateSupplierCommand(
|
||||
string? Email,
|
||||
string? Address,
|
||||
string? ContactPerson,
|
||||
string? Note) : IRequest<Guid>;
|
||||
string? Note,
|
||||
// S? go-live expansion — 18 field bổ sung, tất cả TRAILING OPTIONAL (= null)
|
||||
// để payload tối thiểu cũ + call-site positional 9-arg vẫn hợp lệ (mọi field optional).
|
||||
string? PackageCategory = null,
|
||||
string? OfficeAddress = null,
|
||||
string? MailingAddress = null,
|
||||
string? Fax = null,
|
||||
string? BankAccount = null,
|
||||
string? SecondaryBankAccount = null,
|
||||
string? LegalRepresentative = null,
|
||||
string? LegalRepTitle = null,
|
||||
string? AuthorizationNote = null,
|
||||
string? LinkGuq = null,
|
||||
string? LinkGpkd = null,
|
||||
string? LinkHsnl = null,
|
||||
string? ContactTitle = null,
|
||||
string? ContactPhone = null,
|
||||
string? MailRecipient = null,
|
||||
string? ReferralSource = null,
|
||||
string? OwnerPmh = null,
|
||||
SupplierStatus? Status = null) : IRequest<Guid>;
|
||||
|
||||
public class CreateSupplierCommandValidator : AbstractValidator<CreateSupplierCommand>
|
||||
{
|
||||
@ -31,6 +51,24 @@ public class CreateSupplierCommandValidator : AbstractValidator<CreateSupplierCo
|
||||
RuleFor(x => x.Address).MaximumLength(500);
|
||||
RuleFor(x => x.ContactPerson).MaximumLength(200);
|
||||
RuleFor(x => x.Note).MaximumLength(1000);
|
||||
// S? — new fields OPTIONAL: MaximumLength ONLY, KHÔNG NotEmpty (Create open-auth, minimal payload phải hợp lệ).
|
||||
RuleFor(x => x.PackageCategory).MaximumLength(300);
|
||||
RuleFor(x => x.OfficeAddress).MaximumLength(500);
|
||||
RuleFor(x => x.MailingAddress).MaximumLength(500);
|
||||
RuleFor(x => x.Fax).MaximumLength(50);
|
||||
RuleFor(x => x.BankAccount).MaximumLength(500);
|
||||
RuleFor(x => x.SecondaryBankAccount).MaximumLength(500);
|
||||
RuleFor(x => x.LegalRepresentative).MaximumLength(200);
|
||||
RuleFor(x => x.LegalRepTitle).MaximumLength(150);
|
||||
RuleFor(x => x.AuthorizationNote).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkGuq).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkGpkd).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkHsnl).MaximumLength(1000);
|
||||
RuleFor(x => x.ContactTitle).MaximumLength(150);
|
||||
RuleFor(x => x.ContactPhone).MaximumLength(50);
|
||||
RuleFor(x => x.MailRecipient).MaximumLength(200);
|
||||
RuleFor(x => x.ReferralSource).MaximumLength(300);
|
||||
RuleFor(x => x.OwnerPmh).MaximumLength(150);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +94,24 @@ public class CreateSupplierCommandHandler : IRequestHandler<CreateSupplierComman
|
||||
Address = request.Address,
|
||||
ContactPerson = request.ContactPerson,
|
||||
Note = request.Note,
|
||||
PackageCategory = request.PackageCategory,
|
||||
OfficeAddress = request.OfficeAddress,
|
||||
MailingAddress = request.MailingAddress,
|
||||
Fax = request.Fax,
|
||||
BankAccount = request.BankAccount,
|
||||
SecondaryBankAccount = request.SecondaryBankAccount,
|
||||
LegalRepresentative = request.LegalRepresentative,
|
||||
LegalRepTitle = request.LegalRepTitle,
|
||||
AuthorizationNote = request.AuthorizationNote,
|
||||
LinkGuq = request.LinkGuq,
|
||||
LinkGpkd = request.LinkGpkd,
|
||||
LinkHsnl = request.LinkHsnl,
|
||||
ContactTitle = request.ContactTitle,
|
||||
ContactPhone = request.ContactPhone,
|
||||
MailRecipient = request.MailRecipient,
|
||||
ReferralSource = request.ReferralSource,
|
||||
OwnerPmh = request.OwnerPmh,
|
||||
Status = request.Status,
|
||||
};
|
||||
_db.Suppliers.Add(entity);
|
||||
await _db.SaveChangesAsync(ct);
|
||||
|
||||
@ -17,7 +17,26 @@ public record UpdateSupplierCommand(
|
||||
string? Email,
|
||||
string? Address,
|
||||
string? ContactPerson,
|
||||
string? Note) : IRequest;
|
||||
string? Note,
|
||||
// S? go-live expansion — 18 field bổ sung, tất cả TRAILING OPTIONAL (= null). FE sửa tay gửi đủ field.
|
||||
string? PackageCategory = null,
|
||||
string? OfficeAddress = null,
|
||||
string? MailingAddress = null,
|
||||
string? Fax = null,
|
||||
string? BankAccount = null,
|
||||
string? SecondaryBankAccount = null,
|
||||
string? LegalRepresentative = null,
|
||||
string? LegalRepTitle = null,
|
||||
string? AuthorizationNote = null,
|
||||
string? LinkGuq = null,
|
||||
string? LinkGpkd = null,
|
||||
string? LinkHsnl = null,
|
||||
string? ContactTitle = null,
|
||||
string? ContactPhone = null,
|
||||
string? MailRecipient = null,
|
||||
string? ReferralSource = null,
|
||||
string? OwnerPmh = null,
|
||||
SupplierStatus? Status = null) : IRequest;
|
||||
|
||||
public class UpdateSupplierCommandValidator : AbstractValidator<UpdateSupplierCommand>
|
||||
{
|
||||
@ -28,6 +47,24 @@ public class UpdateSupplierCommandValidator : AbstractValidator<UpdateSupplierCo
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.Type).IsInEnum();
|
||||
RuleFor(x => x.Email).EmailAddress().When(x => !string.IsNullOrWhiteSpace(x.Email));
|
||||
// S? — new fields OPTIONAL: MaximumLength ONLY, KHÔNG NotEmpty.
|
||||
RuleFor(x => x.PackageCategory).MaximumLength(300);
|
||||
RuleFor(x => x.OfficeAddress).MaximumLength(500);
|
||||
RuleFor(x => x.MailingAddress).MaximumLength(500);
|
||||
RuleFor(x => x.Fax).MaximumLength(50);
|
||||
RuleFor(x => x.BankAccount).MaximumLength(500);
|
||||
RuleFor(x => x.SecondaryBankAccount).MaximumLength(500);
|
||||
RuleFor(x => x.LegalRepresentative).MaximumLength(200);
|
||||
RuleFor(x => x.LegalRepTitle).MaximumLength(150);
|
||||
RuleFor(x => x.AuthorizationNote).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkGuq).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkGpkd).MaximumLength(1000);
|
||||
RuleFor(x => x.LinkHsnl).MaximumLength(1000);
|
||||
RuleFor(x => x.ContactTitle).MaximumLength(150);
|
||||
RuleFor(x => x.ContactPhone).MaximumLength(50);
|
||||
RuleFor(x => x.MailRecipient).MaximumLength(200);
|
||||
RuleFor(x => x.ReferralSource).MaximumLength(300);
|
||||
RuleFor(x => x.OwnerPmh).MaximumLength(150);
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +92,24 @@ public class UpdateSupplierCommandHandler : IRequestHandler<UpdateSupplierComman
|
||||
entity.Address = request.Address;
|
||||
entity.ContactPerson = request.ContactPerson;
|
||||
entity.Note = request.Note;
|
||||
entity.PackageCategory = request.PackageCategory;
|
||||
entity.OfficeAddress = request.OfficeAddress;
|
||||
entity.MailingAddress = request.MailingAddress;
|
||||
entity.Fax = request.Fax;
|
||||
entity.BankAccount = request.BankAccount;
|
||||
entity.SecondaryBankAccount = request.SecondaryBankAccount;
|
||||
entity.LegalRepresentative = request.LegalRepresentative;
|
||||
entity.LegalRepTitle = request.LegalRepTitle;
|
||||
entity.AuthorizationNote = request.AuthorizationNote;
|
||||
entity.LinkGuq = request.LinkGuq;
|
||||
entity.LinkGpkd = request.LinkGpkd;
|
||||
entity.LinkHsnl = request.LinkHsnl;
|
||||
entity.ContactTitle = request.ContactTitle;
|
||||
entity.ContactPhone = request.ContactPhone;
|
||||
entity.MailRecipient = request.MailRecipient;
|
||||
entity.ReferralSource = request.ReferralSource;
|
||||
entity.OwnerPmh = request.OwnerPmh;
|
||||
entity.Status = request.Status;
|
||||
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
@ -13,5 +13,23 @@ public record SupplierDto(
|
||||
string? Address,
|
||||
string? ContactPerson,
|
||||
string? Note,
|
||||
string? PackageCategory,
|
||||
string? OfficeAddress,
|
||||
string? MailingAddress,
|
||||
string? Fax,
|
||||
string? BankAccount,
|
||||
string? SecondaryBankAccount,
|
||||
string? LegalRepresentative,
|
||||
string? LegalRepTitle,
|
||||
string? AuthorizationNote,
|
||||
string? LinkGuq,
|
||||
string? LinkGpkd,
|
||||
string? LinkHsnl,
|
||||
string? ContactTitle,
|
||||
string? ContactPhone,
|
||||
string? MailRecipient,
|
||||
string? ReferralSource,
|
||||
string? OwnerPmh,
|
||||
SupplierStatus? Status,
|
||||
DateTime CreatedAt,
|
||||
DateTime? UpdatedAt);
|
||||
|
||||
@ -18,6 +18,10 @@ public class GetSupplierQueryHandler : IRequestHandler<GetSupplierQuery, Supplie
|
||||
{
|
||||
var x = await _db.Suppliers.AsNoTracking().FirstOrDefaultAsync(s => s.Id == request.Id, ct)
|
||||
?? throw new NotFoundException("Supplier", request.Id);
|
||||
return new SupplierDto(x.Id, x.Code, x.Name, x.Type, x.TaxCode, x.Phone, x.Email, x.Address, x.ContactPerson, x.Note, x.CreatedAt, x.UpdatedAt);
|
||||
return new SupplierDto(x.Id, x.Code, x.Name, x.Type, x.TaxCode, x.Phone, x.Email, x.Address, x.ContactPerson, x.Note,
|
||||
x.PackageCategory, x.OfficeAddress, x.MailingAddress, x.Fax, x.BankAccount, x.SecondaryBankAccount,
|
||||
x.LegalRepresentative, x.LegalRepTitle, x.AuthorizationNote, x.LinkGuq, x.LinkGpkd, x.LinkHsnl,
|
||||
x.ContactTitle, x.ContactPhone, x.MailRecipient, x.ReferralSource, x.OwnerPmh, x.Status,
|
||||
x.CreatedAt, x.UpdatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,9 @@ public class ListSuppliersQueryHandler : IRequestHandler<ListSuppliersQuery, Pag
|
||||
.Take(request.PageSize)
|
||||
.Select(x => new SupplierDto(
|
||||
x.Id, x.Code, x.Name, x.Type, x.TaxCode, x.Phone, x.Email, x.Address, x.ContactPerson, x.Note,
|
||||
x.PackageCategory, x.OfficeAddress, x.MailingAddress, x.Fax, x.BankAccount, x.SecondaryBankAccount,
|
||||
x.LegalRepresentative, x.LegalRepTitle, x.AuthorizationNote, x.LinkGuq, x.LinkGpkd, x.LinkHsnl,
|
||||
x.ContactTitle, x.ContactPhone, x.MailRecipient, x.ReferralSource, x.OwnerPmh, x.Status,
|
||||
x.CreatedAt, x.UpdatedAt))
|
||||
.ToListAsync(ct);
|
||||
|
||||
|
||||
@ -10,7 +10,27 @@ public class Supplier : AuditableEntity
|
||||
public string? TaxCode { get; set; } // Mã số thuế
|
||||
public string? Phone { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? ContactPerson { get; set; } // Người liên hệ
|
||||
public string? Note { get; set; }
|
||||
public string? Address { get; set; } // Địa chỉ xuất HĐ (đăng ký)
|
||||
public string? ContactPerson { get; set; } // Người liên hệ chính
|
||||
public string? Note { get; set; } // Ghi chú / lý do blacklist
|
||||
|
||||
// ---- S? go-live expansion (18 field bổ sung, tất cả nullable — import Excel "Database NCC") ----
|
||||
public string? PackageCategory { get; set; } // Gói thầu (free-text)
|
||||
public string? OfficeAddress { get; set; } // Địa chỉ văn phòng
|
||||
public string? MailingAddress { get; set; } // Địa chỉ gửi thư
|
||||
public string? Fax { get; set; }
|
||||
public string? BankAccount { get; set; } // Số TK + tên + CN ngân hàng (composite raw)
|
||||
public string? SecondaryBankAccount { get; set; } // Số TK phụ
|
||||
public string? LegalRepresentative { get; set; } // Người đại diện pháp luật
|
||||
public string? LegalRepTitle { get; set; } // Chức vụ đại diện
|
||||
public string? AuthorizationNote { get; set; } // Giấy ủy quyền (free text)
|
||||
public string? LinkGuq { get; set; } // Link GUQ (NAS path)
|
||||
public string? LinkGpkd { get; set; } // Link GPKD (NAS path)
|
||||
public string? LinkHsnl { get; set; } // Link HSNL (NAS path)
|
||||
public string? ContactTitle { get; set; } // Chức vụ người liên hệ
|
||||
public string? ContactPhone { get; set; } // SĐT chính (người liên hệ)
|
||||
public string? MailRecipient { get; set; } // Người nhận thư / SĐT (composite raw)
|
||||
public string? ReferralSource { get; set; } // Nguồn giới thiệu
|
||||
public string? OwnerPmh { get; set; } // Người phụ trách (PMH)
|
||||
public SupplierStatus? Status { get; set; } // Tình trạng hiện tại (nullable = chưa phân loại)
|
||||
}
|
||||
|
||||
8
src/Backend/SolutionErp.Domain/Master/SupplierStatus.cs
Normal file
8
src/Backend/SolutionErp.Domain/Master/SupplierStatus.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace SolutionErp.Domain.Master;
|
||||
|
||||
public enum SupplierStatus
|
||||
{
|
||||
DangHoatDong = 1, // Đang hoạt động
|
||||
Blacklist = 2, // Blacklist
|
||||
NgungHopTac = 3, // Ngừng hợp tác
|
||||
}
|
||||
@ -7,4 +7,5 @@ public enum SupplierType
|
||||
ToDoi = 3, // TĐ — Tổ đội
|
||||
DonViDichVu = 4, // ĐVDV — Đơn vị dịch vụ
|
||||
ChuDauTu = 5, // CĐT — Chủ đầu tư (đặc biệt, bypass quy trình CCM)
|
||||
CaHai = 6, // Cả hai — vừa NCC vừa NTP (append S? go-live; int 1-5 giữ nguyên trên phiếu cũ)
|
||||
}
|
||||
|
||||
@ -21,6 +21,26 @@ public class SupplierConfiguration : IEntityTypeConfiguration<Supplier>
|
||||
b.Property(x => x.ContactPerson).HasMaxLength(200);
|
||||
b.Property(x => x.Note).HasMaxLength(1000);
|
||||
|
||||
// S? go-live expansion — 18 field bổ sung (all nullable). MaxLength khớp spec §3.
|
||||
b.Property(x => x.PackageCategory).HasMaxLength(300);
|
||||
b.Property(x => x.OfficeAddress).HasMaxLength(500);
|
||||
b.Property(x => x.MailingAddress).HasMaxLength(500);
|
||||
b.Property(x => x.Fax).HasMaxLength(50);
|
||||
b.Property(x => x.BankAccount).HasMaxLength(500);
|
||||
b.Property(x => x.SecondaryBankAccount).HasMaxLength(500);
|
||||
b.Property(x => x.LegalRepresentative).HasMaxLength(200);
|
||||
b.Property(x => x.LegalRepTitle).HasMaxLength(150);
|
||||
b.Property(x => x.AuthorizationNote).HasMaxLength(1000);
|
||||
b.Property(x => x.LinkGuq).HasMaxLength(1000);
|
||||
b.Property(x => x.LinkGpkd).HasMaxLength(1000);
|
||||
b.Property(x => x.LinkHsnl).HasMaxLength(1000);
|
||||
b.Property(x => x.ContactTitle).HasMaxLength(150);
|
||||
b.Property(x => x.ContactPhone).HasMaxLength(50);
|
||||
b.Property(x => x.MailRecipient).HasMaxLength(200);
|
||||
b.Property(x => x.ReferralSource).HasMaxLength(300);
|
||||
b.Property(x => x.OwnerPmh).HasMaxLength(150);
|
||||
b.Property(x => x.Status).HasConversion<int>(); // nullable int enum
|
||||
|
||||
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0"); // Mig 47 (gotcha #57 EXT) — soft-deleted slot reusable, khớp HasQueryFilter !IsDeleted app-check
|
||||
b.HasIndex(x => x.Type);
|
||||
|
||||
|
||||
@ -2765,26 +2765,62 @@ public static class DbInitializer
|
||||
}
|
||||
if (addedWorkItems > 0) logger.LogInformation("Seeded {Count} real work items (S55 import)", addedWorkItems);
|
||||
|
||||
// ---------- SUPPLIERS (4) — (Code, Name, Type, TaxCode, Phone, Email, Address, ContactPerson, Note) ---------- [S96 2026-07-01: +DONGDUONG import từ Excel "Database NCC" anh Kiệt FDC; 15-18 field form (bank chi tiết/địa chỉ VP/link GPKD-HSNL-GUQ/PMH/nguồn GT...) NOTE-lại-từ-từ-tính = backlog schema Supplier mở rộng, xem docs/STATUS Next-up]
|
||||
// ---------- SUPPLIERS (4) — 30-field expansion (S? go-live, Mig 62) ----------
|
||||
// Import Excel "Database NCC" anh Kiệt FDC. 9 field cũ (reuse) + 18 field mới +Status.
|
||||
// Note = null cho cả 4 (đã tách legacy-cram sang các cột riêng; un-cram Sql() trong Mig 62).
|
||||
// Idempotency = insert-if-missing + FILL-NULLS-if-exists: row đã tồn tại → CHỈ set các
|
||||
// cột MỚI đang null (KHÔNG đè non-null) → go-live-safe, không clobber, idempotent.
|
||||
var suppliers = new[]
|
||||
{
|
||||
new Supplier { Code = "TRUONGGIANG", Name = "CÔNG TY CỔ PHẦN NHÔM KÍNH VÀ ĐẦU TƯ TRƯỜNG GIANG", Type = SupplierType.NhaThauPhu, TaxCode = "0312 251 859", Phone = "08 2253 1381", Email = "viethq1985@gmail.com", Address = "Số 77/46A Đường Chuyên Dùng Chính, Phường Phú Thuận, TP.HCM", ContactPerson = "Mr. Việt", Note = "Loại: Nhôm kính, alu; NĐD: Ông: HOÀNG QUỐC VIỆT - GIÁM ĐỐC ĐIỀU HÀNH; TK: 100212127 Ngân hàng Eximbank - Chi Nhánh Hòa Bình; Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "TANPHU", Name = "CÔNG TY TNHH SẢN XUẤT THƯƠNG MẠI SƠN TÂN PHÚ", Type = SupplierType.NhaThauPhu, TaxCode = "0305497393", Phone = "0902 686468", Email = "thu@tanphupaint.com", Address = "Số 49/5A Đường số 7, Khu phố 53, P. Linh Xuân, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Ms Thư", Note = "Loại: Sơn PU, Epoxy, liquid; NĐD: Ông ĐINH PHƯỚC THỌ - GIÁM ĐỐC; TK: 8683107979 tại Ngân hàng BIDV - CN Nam Bình Dương; Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "TGN", Name = "CÔNG TY CỔ PHẦN SIÊU THỊ VLXD THẾ GIỚI NHÀ", Type = SupplierType.NhaCungCap, TaxCode = "3603497972", Phone = "028 36206000", Email = "duong.ltt@tgngroup.vn", Address = "46-48 Nguyễn Cơ Thạch, Phường An Khánh, Thành phố Hồ Chí Minh,Việt Nam", ContactPerson = "Ms. Dương", Note = "Loại: Bê tông; NĐD: Ông TRẦN ANH ĐIỀN - TỔNG GIÁM ĐỐC; TK: 114002641307 tại VietinBank CN KCN Biên Hòa; Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "DONGDUONG", Name = "CÔNG TY CỔ PHẦN DỊCH VỤ CÔNG NGHỆ ĐÔNG DƯƠNG", Type = SupplierType.NhaThauPhu, Phone = "0913 912 094", Email = "hoangmi.tl@dongduongelevator.com", Address = "Số 11 Đường 27 Khu đô thị Vạn Phúc, Phường Hiệp Bình, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Ms.Mi", Note = "Loại: Thang máy, bàn nâng hạ; NĐD: Ông ĐOÀN MẬU QUẾ - GIÁM ĐỐC; TK: 110616498868 tại Ngân hàng TMCP Công thương (Vietinbank) – CN Thủ Thiêm; GUQ: Số 02/2024/QĐ-ĐD ký 17/06/2024 (Bà Đoàn Phan Ngọc Bích - CT HĐQT); Trạng thái: ✅ Đang hoạt động" },
|
||||
new Supplier { Code = "TRUONGGIANG", Name = "CÔNG TY CỔ PHẦN NHÔM KÍNH VÀ ĐẦU TƯ TRƯỜNG GIANG", Type = SupplierType.NhaThauPhu, TaxCode = "0312 251 859", Phone = "08 2253 1381", Email = "viethq1985@gmail.com", Address = "77/46A Đường Chuyên Dùng Chính, Phường Phú Thuận, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Mr. Việt", Note = null, Status = SupplierStatus.DangHoatDong,
|
||||
PackageCategory = "10-Nhôm kính+Face Mặt dựng", BankAccount = "100212127 Ngân hàng Eximbank - Chi Nhánh Hòa Bình", LegalRepresentative = "Ông: HOÀNG QUỐC VIỆT", LegalRepTitle = "GIÁM ĐỐC ĐIỀU HÀNH", AuthorizationNote = "Theo giấy ủy quyền Số 01UQTGD-2022 ký ngày 01/04/2022 từ đại diện pháp luật của công ty là Ông Nguyễn Đăng Khoa, Chức Vụ: Tổng Giám Đốc", LinkGuq = @"01. GPKD_HSNL_GUQ\02.Trường giang\GIẤY ỦY QUYỀN ANH VIỆT.pdf", LinkGpkd = @"01. GPKD_HSNL_GUQ\02.Trường giang\GP TRƯỜNG GIANG 14.11.2024 THAY DOI L6.pdf", ContactTitle = "Giám đốc ĐH", ContactPhone = "0966 801 456", MailingAddress = "D32, ấp 1B, Xã Rạch Kiến, Tỉnh Tây Ninh (địa chỉ cũ: D32 ấp 1B, xã Long Hòa, Huyện Cần Đước, Tỉnh Long An)", MailRecipient = "MS Thủy / 036 405 0016", ReferralSource = "Tái sử dụng DA trước", OwnerPmh = "Bình" },
|
||||
new Supplier { Code = "TANPHU", Name = "CÔNG TY TNHH SẢN XUẤT THƯƠNG MẠI SƠN TÂN PHÚ", Type = SupplierType.NhaThauPhu, TaxCode = "0305497393", Phone = "0902 686468", Email = "thu@tanphupaint.com", Address = "Số 49/5A Đường số 7, Khu phố 53, P. Linh Xuân, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Ms Thư", Note = null, Status = SupplierStatus.DangHoatDong,
|
||||
PackageCategory = "15-Sơn PU/Epoxy/liquid", BankAccount = "8683107979 tại Ngân hàng BIDV - CN Nam Bình Dương", LegalRepresentative = "Ông ĐINH PHƯỚC THỌ", LegalRepTitle = "GIÁM ĐỐC", LinkGpkd = @"01. GPKD_HSNL_GUQ\01.Sơn Tân Phú\BUSINESS LICENSE-TAN PHU PAINT CO LTD(1).pdf", LinkHsnl = @"01. GPKD_HSNL_GUQ\01.Sơn Tân Phú\HỒ SƠ NĂNG LỰC CÔNG TY SƠN TÂN PHÚ-04.2026.pdf", ContactTitle = "Kinh doanh", ContactPhone = "0908355222", ReferralSource = "Qua đối tác", OwnerPmh = "Bình" },
|
||||
new Supplier { Code = "TGN", Name = "CÔNG TY CỔ PHẦN SIÊU THỊ VLXD THẾ GIỚI NHÀ", Type = SupplierType.NhaCungCap, TaxCode = "3603497972", Phone = "028 36206000", Email = "duong.ltt@tgngroup.vn", Address = "46-48 Nguyễn Cơ Thạch, Phường An Khánh, Thành phố Hồ Chí Minh,Việt Nam", ContactPerson = "Ms. Dương", Note = null, Status = SupplierStatus.DangHoatDong,
|
||||
PackageCategory = "2-Bê tông", Fax = "0251.2814663", BankAccount = "114002641307 tại VietinBank CN KCN Biên Hòa", LegalRepresentative = "Ông TRẦN ANH ĐIỀN", LegalRepTitle = "TỔNG GIÁM ĐỐC", LinkGpkd = @"01. GPKD_HSNL_GUQ\03.TGN\GPKD 2026.pdf", LinkHsnl = @"01. GPKD_HSNL_GUQ\03.TGN\HỒ SƠ NĂNG LỰC TGN 2022.pdf", ContactTitle = "Kinh doanh", ContactPhone = "0906.303. 087", MailingAddress = "643 xa lộ hà nội, kp2, p. long bình, đồng nai", MailRecipient = "Ms Dương/ 0967 019 386", ReferralSource = "Tái sử dụng DA trước", OwnerPmh = "Bình" },
|
||||
new Supplier { Code = "DONGDUONG", Name = "CÔNG TY CỔ PHẦN DỊCH VỤ CÔNG NGHỆ ĐÔNG DƯƠNG", Type = SupplierType.NhaThauPhu, TaxCode = null, Phone = "0913 912 094", Email = "hoangmi.tl@dongduongelevator.com", Address = "Số 11 Đường 27 Khu đô thị Vạn Phúc, Phường Hiệp Bình, Thành phố Hồ Chí Minh, Việt Nam", ContactPerson = "Ms.Mi", Note = null, Status = SupplierStatus.DangHoatDong,
|
||||
PackageCategory = "6-Thang máy/ bàn nâng hạ", BankAccount = "110616498868 tại Ngân hàng TMCP Công thương (Vietinbank) – CN Thủ Thiêm", LegalRepresentative = "Ông ĐOÀN MẬU QUẾ", LegalRepTitle = "GIÁM ĐỐC", AuthorizationNote = "Theo giấy ủy quyền Số 02/2024/QĐ-ĐD ký ngày 17/06/2024 từ đại diện pháp luật của công ty là Bà Đoàn Phan Ngọc Bích, Chức Vụ: Chủ Tịch Hồi Đồng Quản Trị", LinkGuq = @"01. GPKD_HSNL_GUQ\04. Đông Dương\GIẤY ỦY QUYỀN GIÁM ĐỐC_MR.QUẾ_NĂM 2026.pdf", LinkGpkd = @"01. GPKD_HSNL_GUQ\04. Đông Dương\4. LẦN THỨ 3_GIẤY ĐKKD_CÔNG NGHỆ ĐÔNG DƯƠNG_02.04.2026.pdf", LinkHsnl = @"01. GPKD_HSNL_GUQ\04. Đông Dương\HỒ SƠ NĂNG LỰC 2025.pdf", ContactTitle = "Kinh doanh", ContactPhone = "0903.632.625", MailRecipient = "Ms Mi/0903.632.625", ReferralSource = "Tái sử dụng DA trước", OwnerPmh = "Bình" },
|
||||
};
|
||||
|
||||
var existingSupplierCodes = await db.Suppliers.Select(s => s.Code).ToListAsync();
|
||||
// Fetch existing rows FULL (không chỉ Code) để fill-nulls trên các cột mới.
|
||||
var existingSuppliers = await db.Suppliers.ToListAsync();
|
||||
var existingByCode = existingSuppliers.ToDictionary(s => s.Code, StringComparer.Ordinal);
|
||||
int addedSuppliers = 0;
|
||||
int filledSuppliers = 0;
|
||||
foreach (var s in suppliers)
|
||||
{
|
||||
if (existingSupplierCodes.Contains(s.Code)) continue;
|
||||
if (existingByCode.TryGetValue(s.Code, out var e))
|
||||
{
|
||||
// FILL-NULLS: chỉ set cột MỚI đang null (KHÔNG đè non-null; KHÔNG đụng 9 cột cũ).
|
||||
bool changed = false;
|
||||
if (e.PackageCategory is null && s.PackageCategory is not null) { e.PackageCategory = s.PackageCategory; changed = true; }
|
||||
if (e.OfficeAddress is null && s.OfficeAddress is not null) { e.OfficeAddress = s.OfficeAddress; changed = true; }
|
||||
if (e.MailingAddress is null && s.MailingAddress is not null) { e.MailingAddress = s.MailingAddress; changed = true; }
|
||||
if (e.Fax is null && s.Fax is not null) { e.Fax = s.Fax; changed = true; }
|
||||
if (e.BankAccount is null && s.BankAccount is not null) { e.BankAccount = s.BankAccount; changed = true; }
|
||||
if (e.SecondaryBankAccount is null && s.SecondaryBankAccount is not null) { e.SecondaryBankAccount = s.SecondaryBankAccount; changed = true; }
|
||||
if (e.LegalRepresentative is null && s.LegalRepresentative is not null) { e.LegalRepresentative = s.LegalRepresentative; changed = true; }
|
||||
if (e.LegalRepTitle is null && s.LegalRepTitle is not null) { e.LegalRepTitle = s.LegalRepTitle; changed = true; }
|
||||
if (e.AuthorizationNote is null && s.AuthorizationNote is not null) { e.AuthorizationNote = s.AuthorizationNote; changed = true; }
|
||||
if (e.LinkGuq is null && s.LinkGuq is not null) { e.LinkGuq = s.LinkGuq; changed = true; }
|
||||
if (e.LinkGpkd is null && s.LinkGpkd is not null) { e.LinkGpkd = s.LinkGpkd; changed = true; }
|
||||
if (e.LinkHsnl is null && s.LinkHsnl is not null) { e.LinkHsnl = s.LinkHsnl; changed = true; }
|
||||
if (e.ContactTitle is null && s.ContactTitle is not null) { e.ContactTitle = s.ContactTitle; changed = true; }
|
||||
if (e.ContactPhone is null && s.ContactPhone is not null) { e.ContactPhone = s.ContactPhone; changed = true; }
|
||||
if (e.MailRecipient is null && s.MailRecipient is not null) { e.MailRecipient = s.MailRecipient; changed = true; }
|
||||
if (e.ReferralSource is null && s.ReferralSource is not null) { e.ReferralSource = s.ReferralSource; changed = true; }
|
||||
if (e.OwnerPmh is null && s.OwnerPmh is not null) { e.OwnerPmh = s.OwnerPmh; changed = true; }
|
||||
if (e.Status is null && s.Status is not null) { e.Status = s.Status; changed = true; }
|
||||
if (changed) filledSuppliers++;
|
||||
continue;
|
||||
}
|
||||
db.Suppliers.Add(s);
|
||||
addedSuppliers++;
|
||||
}
|
||||
if (addedSuppliers > 0) logger.LogInformation("Seeded {Count} real suppliers (S55 import)", addedSuppliers);
|
||||
if (filledSuppliers > 0) logger.LogInformation("Filled new columns on {Count} existing suppliers (S? 30-field)", filledSuppliers);
|
||||
|
||||
if (addedProjects + addedWorkItems + addedSuppliers > 0)
|
||||
if (addedProjects + addedWorkItems + addedSuppliers + filledSuppliers > 0)
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,223 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ExpandSupplierFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AuthorizationNote",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(1000)",
|
||||
maxLength: 1000,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "BankAccount",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ContactPhone",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ContactTitle",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(150)",
|
||||
maxLength: 150,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Fax",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LegalRepTitle",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(150)",
|
||||
maxLength: 150,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LegalRepresentative",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LinkGpkd",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(1000)",
|
||||
maxLength: 1000,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LinkGuq",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(1000)",
|
||||
maxLength: 1000,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LinkHsnl",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(1000)",
|
||||
maxLength: 1000,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MailRecipient",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MailingAddress",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OfficeAddress",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OwnerPmh",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(150)",
|
||||
maxLength: 150,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PackageCategory",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(300)",
|
||||
maxLength: 300,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ReferralSource",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(300)",
|
||||
maxLength: 300,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SecondaryBankAccount",
|
||||
table: "Suppliers",
|
||||
type: "nvarchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Status",
|
||||
table: "Suppliers",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
// One-time un-cram: clear the legacy concatenated Note on the 4 seeded suppliers now that
|
||||
// their data lives in structured columns. Guarded exact-match prefix (Note LIKE N'Loại:%')
|
||||
// so manually-edited Notes are never touched (spec §4; safe on existing prod rows only).
|
||||
migrationBuilder.Sql(
|
||||
"UPDATE Suppliers SET Note = NULL " +
|
||||
"WHERE Code IN (N'TRUONGGIANG', N'TANPHU', N'TGN', N'DONGDUONG') " +
|
||||
"AND Note LIKE N'Loại:%';");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AuthorizationNote",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "BankAccount",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContactPhone",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContactTitle",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Fax",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LegalRepTitle",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LegalRepresentative",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LinkGpkd",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LinkGuq",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LinkHsnl",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MailRecipient",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MailingAddress",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OfficeAddress",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OwnerPmh",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PackageCategory",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ReferralSource",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SecondaryBankAccount",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Status",
|
||||
table: "Suppliers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3218,6 +3218,14 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("AuthorizationNote")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("BankAccount")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
@ -3227,6 +3235,14 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<string>("ContactPhone")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("ContactTitle")
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("nvarchar(150)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
@ -3243,9 +3259,41 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Fax")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("LegalRepTitle")
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("nvarchar(150)");
|
||||
|
||||
b.Property<string>("LegalRepresentative")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<string>("LinkGpkd")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("LinkGuq")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("LinkHsnl")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("MailRecipient")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)");
|
||||
|
||||
b.Property<string>("MailingAddress")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
@ -3255,10 +3303,33 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<string>("OfficeAddress")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("OwnerPmh")
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("nvarchar(150)");
|
||||
|
||||
b.Property<string>("PackageCategory")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("nvarchar(30)");
|
||||
|
||||
b.Property<string>("ReferralSource")
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("nvarchar(300)");
|
||||
|
||||
b.Property<string>("SecondaryBankAccount")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int?>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TaxCode")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
Reference in New Issue
Block a user