Compare commits
8 Commits
main
...
Ashutosh_B
Author | SHA1 | Date | |
---|---|---|---|
5ce95f7e23 | |||
636dd48ad6 | |||
62688d508f | |||
c92e71b292 | |||
2b19890b53 | |||
5ff87cd870 | |||
b30369baa5 | |||
4684b438f6 |
@ -1,6 +1,7 @@
|
|||||||
using Marco.Pms.Model.Activities;
|
using Marco.Pms.Model.Activities;
|
||||||
using Marco.Pms.Model.AttendanceModule;
|
using Marco.Pms.Model.AttendanceModule;
|
||||||
using Marco.Pms.Model.Authentication;
|
using Marco.Pms.Model.Authentication;
|
||||||
|
using Marco.Pms.Model.Collection;
|
||||||
using Marco.Pms.Model.Directory;
|
using Marco.Pms.Model.Directory;
|
||||||
using Marco.Pms.Model.DocumentManager;
|
using Marco.Pms.Model.DocumentManager;
|
||||||
using Marco.Pms.Model.Employees;
|
using Marco.Pms.Model.Employees;
|
||||||
@ -133,6 +134,12 @@ namespace Marco.Pms.DataAccess.Data
|
|||||||
public DbSet<ProjectServiceMapping> ProjectServiceMappings { get; set; }
|
public DbSet<ProjectServiceMapping> ProjectServiceMappings { get; set; }
|
||||||
public DbSet<ProjectOrgMapping> ProjectOrgMappings { get; set; }
|
public DbSet<ProjectOrgMapping> ProjectOrgMappings { get; set; }
|
||||||
|
|
||||||
|
// Collection
|
||||||
|
public DbSet<Invoice> Invoices { get; set; }
|
||||||
|
public DbSet<InvoiceComment> InvoiceComments { get; set; }
|
||||||
|
public DbSet<InvoiceAttachment> InvoiceAttachments { get; set; }
|
||||||
|
public DbSet<ReceivedInvoicePayment> ReceivedInvoicePayments { get; set; }
|
||||||
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
6505
Marco.Pms.DataAccess/Migrations/20251013122515_Added_Collection_Related_Tables.Designer.cs
generated
Normal file
6505
Marco.Pms.DataAccess/Migrations/20251013122515_Added_Collection_Related_Tables.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,256 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Marco.Pms.DataAccess.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Added_Collection_Related_Tables : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Invoices",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
Title = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
Description = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
InvoiceNumber = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
ProjectId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
InvoiceDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
ClientSubmitedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
ExceptedPaymentDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
Amount = table.Column<double>(type: "double", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||||
|
UpdatedById = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Invoices", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Invoices_Employees_CreatedById",
|
||||||
|
column: x => x.CreatedById,
|
||||||
|
principalTable: "Employees",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Invoices_Employees_UpdatedById",
|
||||||
|
column: x => x.UpdatedById,
|
||||||
|
principalTable: "Employees",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Invoices_Projects_ProjectId",
|
||||||
|
column: x => x.ProjectId,
|
||||||
|
principalTable: "Projects",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Invoices_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "InvoiceAttachments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
InvoiceId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
DocumentId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_InvoiceAttachments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceAttachments_Documents_DocumentId",
|
||||||
|
column: x => x.DocumentId,
|
||||||
|
principalTable: "Documents",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceAttachments_Invoices_InvoiceId",
|
||||||
|
column: x => x.InvoiceId,
|
||||||
|
principalTable: "Invoices",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceAttachments_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "InvoiceComments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
Comment = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
InvoiceId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_InvoiceComments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceComments_Employees_CreatedById",
|
||||||
|
column: x => x.CreatedById,
|
||||||
|
principalTable: "Employees",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceComments_Invoices_InvoiceId",
|
||||||
|
column: x => x.InvoiceId,
|
||||||
|
principalTable: "Invoices",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_InvoiceComments_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ReceivedInvoicePayments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
InvoiceId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
PaymentReceivedDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
TransactionId = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
Amount = table.Column<double>(type: "double", nullable: false),
|
||||||
|
IsActive = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||||
|
CreatedById = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ReceivedInvoicePayments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ReceivedInvoicePayments_Employees_CreatedById",
|
||||||
|
column: x => x.CreatedById,
|
||||||
|
principalTable: "Employees",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ReceivedInvoicePayments_Invoices_InvoiceId",
|
||||||
|
column: x => x.InvoiceId,
|
||||||
|
principalTable: "Invoices",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ReceivedInvoicePayments_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceAttachments_DocumentId",
|
||||||
|
table: "InvoiceAttachments",
|
||||||
|
column: "DocumentId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceAttachments_InvoiceId",
|
||||||
|
table: "InvoiceAttachments",
|
||||||
|
column: "InvoiceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceAttachments_TenantId",
|
||||||
|
table: "InvoiceAttachments",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceComments_CreatedById",
|
||||||
|
table: "InvoiceComments",
|
||||||
|
column: "CreatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceComments_InvoiceId",
|
||||||
|
table: "InvoiceComments",
|
||||||
|
column: "InvoiceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_InvoiceComments_TenantId",
|
||||||
|
table: "InvoiceComments",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Invoices_CreatedById",
|
||||||
|
table: "Invoices",
|
||||||
|
column: "CreatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Invoices_ProjectId",
|
||||||
|
table: "Invoices",
|
||||||
|
column: "ProjectId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Invoices_TenantId",
|
||||||
|
table: "Invoices",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Invoices_UpdatedById",
|
||||||
|
table: "Invoices",
|
||||||
|
column: "UpdatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ReceivedInvoicePayments_CreatedById",
|
||||||
|
table: "ReceivedInvoicePayments",
|
||||||
|
column: "CreatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ReceivedInvoicePayments_InvoiceId",
|
||||||
|
table: "ReceivedInvoicePayments",
|
||||||
|
column: "InvoiceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ReceivedInvoicePayments_TenantId",
|
||||||
|
table: "ReceivedInvoicePayments",
|
||||||
|
column: "TenantId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "InvoiceAttachments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "InvoiceComments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ReceivedInvoicePayments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Invoices");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6514
Marco.Pms.DataAccess/Migrations/20251013141456_Added_EInvoiceNumber_In_Invoice_Table.Designer.cs
generated
Normal file
6514
Marco.Pms.DataAccess/Migrations/20251013141456_Added_EInvoiceNumber_In_Invoice_Table.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,61 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Marco.Pms.DataAccess.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Added_EInvoiceNumber_In_Invoice_Table : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "Amount",
|
||||||
|
table: "Invoices",
|
||||||
|
newName: "TaxAmount");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "BasicAmount",
|
||||||
|
table: "Invoices",
|
||||||
|
type: "double",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0.0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "EInvoiceNumber",
|
||||||
|
table: "Invoices",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "MarkAsCompleted",
|
||||||
|
table: "Invoices",
|
||||||
|
type: "tinyint(1)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BasicAmount",
|
||||||
|
table: "Invoices");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EInvoiceNumber",
|
||||||
|
table: "Invoices");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MarkAsCompleted",
|
||||||
|
table: "Invoices");
|
||||||
|
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "TaxAmount",
|
||||||
|
table: "Invoices",
|
||||||
|
newName: "Amount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -369,6 +369,180 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.ToTable("RefreshTokens");
|
b.ToTable("RefreshTokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.Invoice", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<double>("BasicAmount")
|
||||||
|
.HasColumnType("double");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ClientSubmitedDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedById")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("EInvoiceNumber")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExceptedPaymentDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("InvoiceDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("InvoiceNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("MarkAsCompleted")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<double>("TaxAmount")
|
||||||
|
.HasColumnType("double");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedById")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedById");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.HasIndex("UpdatedById");
|
||||||
|
|
||||||
|
b.ToTable("Invoices");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceAttachment", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("DocumentId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("InvoiceId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DocumentId");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("InvoiceAttachments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceComment", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Comment")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedById")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("InvoiceId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedById");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("InvoiceComments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.ReceivedInvoicePayment", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<double>("Amount")
|
||||||
|
.HasColumnType("double");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedById")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("InvoiceId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("PaymentReceivedDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("TransactionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedById");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("ReceivedInvoicePayments");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
|
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -4794,6 +4968,120 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.Invoice", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedById")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Projects.Project", "Project")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProjectId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "UpdatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UpdatedById");
|
||||||
|
|
||||||
|
b.Navigation("CreatedBy");
|
||||||
|
|
||||||
|
b.Navigation("Project");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
|
||||||
|
b.Navigation("UpdatedBy");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceAttachment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Marco.Pms.Model.DocumentManager.Document", "Document")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DocumentId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("InvoiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Document");
|
||||||
|
|
||||||
|
b.Navigation("Invoice");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.InvoiceComment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedById")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("InvoiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("CreatedBy");
|
||||||
|
|
||||||
|
b.Navigation("Invoice");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Collection.ReceivedInvoicePayment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedById")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.Collection.Invoice", "Invoice")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("InvoiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("CreatedBy");
|
||||||
|
|
||||||
|
b.Navigation("Invoice");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
|
modelBuilder.Entity("Marco.Pms.Model.Directory.Bucket", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
|
b.HasOne("Marco.Pms.Model.Employees.Employee", "CreatedBy")
|
||||||
|
41
Marco.Pms.Model/Collection/Invoice.cs
Normal file
41
Marco.Pms.Model/Collection/Invoice.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Marco.Pms.Model.Employees;
|
||||||
|
using Marco.Pms.Model.Projects;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Collection
|
||||||
|
{
|
||||||
|
public class Invoice : TenantRelation
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; } = default!;
|
||||||
|
public string Description { get; set; } = default!;
|
||||||
|
public string InvoiceNumber { get; set; } = default!;
|
||||||
|
public string? EInvoiceNumber { get; set; }
|
||||||
|
public Guid ProjectId { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("ProjectId")]
|
||||||
|
public Project? Project { get; set; }
|
||||||
|
public DateTime InvoiceDate { get; set; }
|
||||||
|
public DateTime ClientSubmitedDate { get; set; }
|
||||||
|
public DateTime ExceptedPaymentDate { get; set; }
|
||||||
|
public double BasicAmount { get; set; }
|
||||||
|
public double TaxAmount { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public bool MarkAsCompleted { get; set; } = true;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public Guid CreatedById { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("CreatedById")]
|
||||||
|
public Employee? CreatedBy { get; set; }
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
public Guid? UpdatedById { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("UpdatedById")]
|
||||||
|
public Employee? UpdatedBy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
22
Marco.Pms.Model/Collection/InvoiceAttachment.cs
Normal file
22
Marco.Pms.Model/Collection/InvoiceAttachment.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Marco.Pms.Model.DocumentManager;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceAttachment : TenantRelation
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("InvoiceId")]
|
||||||
|
public Invoice? Invoice { get; set; }
|
||||||
|
public Guid DocumentId { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("DocumentId")]
|
||||||
|
public Document? Document { get; set; }
|
||||||
|
}
|
||||||
|
}
|
24
Marco.Pms.Model/Collection/InvoiceComment.cs
Normal file
24
Marco.Pms.Model/Collection/InvoiceComment.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using Marco.Pms.Model.Employees;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceComment : TenantRelation
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Comment { get; set; } = default!;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public Guid CreatedById { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("CreatedById")]
|
||||||
|
public Employee? CreatedBy { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("InvoiceId")]
|
||||||
|
public Invoice? Invoice { get; set; }
|
||||||
|
}
|
||||||
|
}
|
27
Marco.Pms.Model/Collection/ReceivedInvoicePayment.cs
Normal file
27
Marco.Pms.Model/Collection/ReceivedInvoicePayment.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Marco.Pms.Model.Employees;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Collection
|
||||||
|
{
|
||||||
|
public class ReceivedInvoicePayment : TenantRelation
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("InvoiceId")]
|
||||||
|
public Invoice? Invoice { get; set; }
|
||||||
|
public DateTime PaymentReceivedDate { get; set; }
|
||||||
|
public string TransactionId { get; set; } = default!;
|
||||||
|
public double Amount { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public Guid CreatedById { get; set; }
|
||||||
|
|
||||||
|
[ValidateNever]
|
||||||
|
[ForeignKey("CreatedById")]
|
||||||
|
public Employee? CreatedBy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
8
Marco.Pms.Model/Dtos/Collection/InvoiceCommentDto.cs
Normal file
8
Marco.Pms.Model/Dtos/Collection/InvoiceCommentDto.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Marco.Pms.Model.Dtos.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceCommentDto
|
||||||
|
{
|
||||||
|
public required string Comment { get; set; }
|
||||||
|
public required Guid InvoiceId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
20
Marco.Pms.Model/Dtos/Collection/InvoiceDto.cs
Normal file
20
Marco.Pms.Model/Dtos/Collection/InvoiceDto.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.Dtos.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceDto
|
||||||
|
{
|
||||||
|
public Guid? Id { get; set; }
|
||||||
|
public required string Title { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public required string InvoiceNumber { get; set; }
|
||||||
|
public string? EInvoiceNumber { get; set; }
|
||||||
|
public required Guid ProjectId { get; set; }
|
||||||
|
public required DateTime InvoiceDate { get; set; }
|
||||||
|
public required DateTime ClientSubmitedDate { get; set; }
|
||||||
|
public required DateTime ExceptedPaymentDate { get; set; }
|
||||||
|
public double BasicAmount { get; set; }
|
||||||
|
public double TaxAmount { get; set; }
|
||||||
|
public List<FileUploadModel>? Attachments { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
Marco.Pms.Model/Dtos/Collection/ReceivedInvoicePaymentDto.cs
Normal file
11
Marco.Pms.Model/Dtos/Collection/ReceivedInvoicePaymentDto.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Marco.Pms.Model.Dtos.Collection
|
||||||
|
{
|
||||||
|
public class ReceivedInvoicePaymentDto
|
||||||
|
{
|
||||||
|
public Guid? Id { get; set; }
|
||||||
|
public required Guid InvoiceId { get; set; }
|
||||||
|
public required DateTime PaymentReceivedDate { get; set; }
|
||||||
|
public required string TransactionId { get; set; } = default!;
|
||||||
|
public required double Amount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
Marco.Pms.Model/ViewModels/Collection/InvoiceAttachmentVM.cs
Normal file
14
Marco.Pms.Model/ViewModels/Collection/InvoiceAttachmentVM.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceAttachmentVM
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
public Guid DocumentId { get; set; }
|
||||||
|
public string? PreSignedUrl { get; set; }
|
||||||
|
public BasicEmployeeVM? UploadedBy { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
13
Marco.Pms.Model/ViewModels/Collection/InvoiceCommentVM.cs
Normal file
13
Marco.Pms.Model/ViewModels/Collection/InvoiceCommentVM.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceCommentVM
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Comment { get; set; } = default!;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
Marco.Pms.Model/ViewModels/Collection/InvoiceDetailsVM.cs
Normal file
31
Marco.Pms.Model/ViewModels/Collection/InvoiceDetailsVM.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Projects;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceDetailsVM
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; } = default!;
|
||||||
|
public string Description { get; set; } = default!;
|
||||||
|
public string InvoiceNumber { get; set; } = default!;
|
||||||
|
public string? EInvoiceNumber { get; set; }
|
||||||
|
public BasicProjectVM? Project { get; set; }
|
||||||
|
public DateTime InvoiceDate { get; set; }
|
||||||
|
public DateTime ClientSubmitedDate { get; set; }
|
||||||
|
public DateTime ExceptedPaymentDate { get; set; }
|
||||||
|
public double BasicAmount { get; set; }
|
||||||
|
public double TaxAmount { get; set; }
|
||||||
|
public double BalanceAmount { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public bool MarkAsCompleted { get; set; } = true;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||||
|
public List<InvoiceAttachmentVM>? Attachments { get; set; }
|
||||||
|
public List<ReceivedInvoicePaymentVM>? ReceivedInvoicePayments { get; set; }
|
||||||
|
public List<InvoiceCommentVM>? Comments { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
27
Marco.Pms.Model/ViewModels/Collection/InvoiceListVM.cs
Normal file
27
Marco.Pms.Model/ViewModels/Collection/InvoiceListVM.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Projects;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Collection
|
||||||
|
{
|
||||||
|
public class InvoiceListVM
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; } = default!;
|
||||||
|
public string Description { get; set; } = default!;
|
||||||
|
public string InvoiceNumber { get; set; } = default!;
|
||||||
|
public string? EInvoiceNumber { get; set; }
|
||||||
|
public BasicProjectVM? Project { get; set; }
|
||||||
|
public DateTime InvoiceDate { get; set; }
|
||||||
|
public DateTime ClientSubmitedDate { get; set; }
|
||||||
|
public DateTime ExceptedPaymentDate { get; set; }
|
||||||
|
public double BasicAmount { get; set; }
|
||||||
|
public double TaxAmount { get; set; }
|
||||||
|
public double BalanceAmount { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public bool MarkAsCompleted { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? UpdatedBy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Model.ViewModels.Collection
|
||||||
|
{
|
||||||
|
public class ReceivedInvoicePaymentVM
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid InvoiceId { get; set; }
|
||||||
|
public DateTime PaymentReceivedDate { get; set; }
|
||||||
|
public string TransactionId { get; set; } = default!;
|
||||||
|
public double Amount { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public BasicEmployeeVM? CreatedBy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -321,7 +321,6 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("project/team")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves employee attendance records for a specified project and date.
|
/// Retrieves employee attendance records for a specified project and date.
|
||||||
/// The result is filtered based on the logged-in employee's permissions (Team or Self).
|
/// The result is filtered based on the logged-in employee's permissions (Team or Self).
|
||||||
@ -331,6 +330,8 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
/// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param>
|
/// <param name="includeInactive">Optional. Includes inactive employees in the team list if true.</param>
|
||||||
/// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param>
|
/// <param name="date">Optional. The date for which to fetch attendance, in "yyyy-MM-dd" format. Defaults to the current UTC date.</param>
|
||||||
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
|
/// <returns>An IActionResult containing a list of employee attendance records or an error response.</returns>
|
||||||
|
|
||||||
|
[HttpGet("project/team")]
|
||||||
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
|
public async Task<IActionResult> EmployeeAttendanceByProjectAsync([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool includeInactive, [FromQuery] string? date = null)
|
||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
@ -390,7 +391,6 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("regularize")]
|
[HttpGet("regularize")]
|
||||||
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
|
public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] Guid? organizationId, [FromQuery] bool IncludeInActive)
|
||||||
{
|
{
|
||||||
@ -456,8 +456,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost("record")]
|
||||||
[Route("record")]
|
|
||||||
public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot)
|
public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
@ -612,7 +611,8 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
LastName = employee.LastName,
|
LastName = employee.LastName,
|
||||||
Id = attendance.Id,
|
Id = attendance.Id,
|
||||||
Activity = attendance.Activity,
|
Activity = attendance.Activity,
|
||||||
JobRoleName = employee.JobRole.Name
|
JobRoleName = employee.JobRole.Name,
|
||||||
|
OrganizationName = employee.Organization?.Name
|
||||||
};
|
};
|
||||||
var sendActivity = 0;
|
var sendActivity = 0;
|
||||||
if (recordAttendanceDot.Id == Guid.Empty)
|
if (recordAttendanceDot.Id == Guid.Empty)
|
||||||
|
690
Marco.Pms.Services/Controllers/CollectionController.cs
Normal file
690
Marco.Pms.Services/Controllers/CollectionController.cs
Normal file
@ -0,0 +1,690 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Marco.Pms.DataAccess.Data;
|
||||||
|
using Marco.Pms.Model.Collection;
|
||||||
|
using Marco.Pms.Model.DocumentManager;
|
||||||
|
using Marco.Pms.Model.Dtos.Collection;
|
||||||
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Collection;
|
||||||
|
using Marco.Pms.Model.ViewModels.Projects;
|
||||||
|
using Marco.Pms.Services.Service;
|
||||||
|
using MarcoBMS.Services.Helpers;
|
||||||
|
using MarcoBMS.Services.Service;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Services.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
public class CollectionController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
private readonly UserHelper _userHelper;
|
||||||
|
private readonly S3UploadService _s3Service;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ILoggingService _logger;
|
||||||
|
private readonly Guid tenantId;
|
||||||
|
public CollectionController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||||
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
|
S3UploadService s3Service,
|
||||||
|
UserHelper userhelper,
|
||||||
|
ILoggingService logger,
|
||||||
|
IMapper mapper)
|
||||||
|
{
|
||||||
|
_dbContextFactory = dbContextFactory;
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
|
_userHelper = userhelper;
|
||||||
|
_s3Service = s3Service;
|
||||||
|
_mapper = mapper;
|
||||||
|
_logger = logger;
|
||||||
|
tenantId = userhelper.GetTenantId();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("invoice/list")]
|
||||||
|
public async Task<IActionResult> GetInvoiceListAsync([FromQuery] string? searchString, [FromQuery] DateTime? fromDate, [FromQuery] DateTime? toDate, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1
|
||||||
|
, [FromQuery] bool isActive = true, [FromQuery] bool isPending = false)
|
||||||
|
{
|
||||||
|
_logger.LogInfo(
|
||||||
|
"Fetching invoice list: Page {PageNumber}, Size {PageSize}, Active={IsActive}, PendingOnly={IsPending}, Search='{SearchString}', From={From}, To={To}",
|
||||||
|
pageNumber, pageSize, isActive, isPending, searchString ?? "", fromDate?.Date ?? DateTime.MinValue, toDate?.Date ?? DateTime.MaxValue);
|
||||||
|
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
// Build base query with required includes and no tracking
|
||||||
|
var invoicesQuery = _context.Invoices
|
||||||
|
.Include(i => i.Project)
|
||||||
|
.Include(i => i.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.Include(i => i.UpdatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.Where(i => i.IsActive == isActive && i.TenantId == tenantId)
|
||||||
|
.AsNoTracking(); // Disable change tracking for read-only query
|
||||||
|
|
||||||
|
// Apply date filter
|
||||||
|
if (fromDate.HasValue && toDate.HasValue)
|
||||||
|
{
|
||||||
|
var fromDateUtc = fromDate.Value.Date;
|
||||||
|
var toDateUtc = toDate.Value.Date.AddDays(1).AddTicks(-1); // End of day
|
||||||
|
invoicesQuery = invoicesQuery.Where(i => i.InvoiceDate >= fromDateUtc && i.InvoiceDate <= toDateUtc);
|
||||||
|
_logger.LogDebug("Applied date filter: {From} to {To}", fromDateUtc, toDateUtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply search filter
|
||||||
|
if (!string.IsNullOrWhiteSpace(searchString))
|
||||||
|
{
|
||||||
|
invoicesQuery = invoicesQuery.Where(i => i.Title.Contains(searchString) || i.InvoiceNumber.Contains(searchString));
|
||||||
|
_logger.LogDebug("Applied search filter with term: {SearchString}", searchString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total count before pagination
|
||||||
|
var totalEntites = await invoicesQuery.CountAsync();
|
||||||
|
_logger.LogDebug("Total matching invoices: {TotalCount}", totalEntites);
|
||||||
|
|
||||||
|
// Apply sorting and pagination
|
||||||
|
var invoices = await invoicesQuery
|
||||||
|
.OrderByDescending(i => i.InvoiceDate)
|
||||||
|
.Skip((pageNumber - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!invoices.Any())
|
||||||
|
{
|
||||||
|
_logger.LogInfo("No invoices found for the given criteria.");
|
||||||
|
var emptyResponse = new
|
||||||
|
{
|
||||||
|
CurrentPage = pageNumber,
|
||||||
|
TotalPages = 0,
|
||||||
|
TotalEntites = 0,
|
||||||
|
Data = new List<InvoiceListVM>()
|
||||||
|
};
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(emptyResponse, "No invoices found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all related payment data in a single query
|
||||||
|
var invoiceIds = invoices.Select(i => i.Id).ToList();
|
||||||
|
var paymentGroups = await _context.ReceivedInvoicePayments
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(rip => invoiceIds.Contains(rip.InvoiceId) && rip.TenantId == tenantId)
|
||||||
|
.GroupBy(rip => rip.InvoiceId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
InvoiceId = g.Key,
|
||||||
|
PaidAmount = g.Sum(rip => rip.Amount)
|
||||||
|
})
|
||||||
|
.ToDictionaryAsync(x => x.InvoiceId, x => x.PaidAmount);
|
||||||
|
|
||||||
|
_logger.LogDebug("Fetched payment data for {Count} invoices", paymentGroups.Count);
|
||||||
|
|
||||||
|
// Map and calculate balance in memory
|
||||||
|
var results = new List<InvoiceListVM>();
|
||||||
|
foreach (var invoice in invoices)
|
||||||
|
{
|
||||||
|
var totalAmount = invoice.BasicAmount + invoice.TaxAmount;
|
||||||
|
var paidAmount = paymentGroups.GetValueOrDefault(invoice.Id, 0);
|
||||||
|
var balanceAmount = totalAmount - paidAmount;
|
||||||
|
|
||||||
|
// Skip if filtering for pending invoices and balance is zero
|
||||||
|
if (isPending && balanceAmount <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var result = _mapper.Map<InvoiceListVM>(invoice);
|
||||||
|
result.BalanceAmount = balanceAmount;
|
||||||
|
results.Add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalPages = (int)Math.Ceiling((double)totalEntites / pageSize);
|
||||||
|
var response = new
|
||||||
|
{
|
||||||
|
CurrentPage = pageNumber,
|
||||||
|
TotalPages = totalPages,
|
||||||
|
TotalEntites = totalEntites,
|
||||||
|
Data = results
|
||||||
|
};
|
||||||
|
|
||||||
|
_logger.LogInfo("Successfully returned {ResultCount} invoices out of {TotalCount} total", results.Count, totalEntites);
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(response, $"{results.Count} invoices fetched successfully"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves complete details of a specific invoice including associated comments, attachments, and payments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The unique identifier of the invoice.</param>
|
||||||
|
/// <returns>Returns invoice details with associated data or a NotFound/BadRequest response.</returns>
|
||||||
|
|
||||||
|
[HttpGet("invoice/details/{id}")]
|
||||||
|
public async Task<IActionResult> GetInvoiceDetailsAsync(Guid id)
|
||||||
|
{
|
||||||
|
_logger.LogInfo("Fetching details for InvoiceId: {InvoiceId}, TenantId: {TenantId}", id, tenantId);
|
||||||
|
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
// Retrieve primary invoice details with related entities (project, created/updated by + roles)
|
||||||
|
var invoice = await context.Invoices
|
||||||
|
.Include(i => i.Project)
|
||||||
|
.Include(i => i.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.Include(i => i.UpdatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(i => i.Id == id && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (invoice == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice not found. InvoiceId: {InvoiceId}, TenantId: {TenantId}", id, tenantId);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Invoice not found", "The specified invoice does not exist.", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInfo("Invoice {InvoiceId} found. Fetching related data...", id);
|
||||||
|
|
||||||
|
// Parallelize loading of child collections using independent DbContext instances — efficient and thread-safe
|
||||||
|
var commentsTask = LoadInvoiceCommentsAsync(id, tenantId);
|
||||||
|
var attachmentsTask = LoadInvoiceAttachmentsAsync(id, tenantId);
|
||||||
|
var paymentsTask = LoadReceivedInvoicePaymentsAsync(id, tenantId);
|
||||||
|
|
||||||
|
await Task.WhenAll(commentsTask, attachmentsTask, paymentsTask);
|
||||||
|
|
||||||
|
var comments = commentsTask.Result;
|
||||||
|
var attachments = attachmentsTask.Result;
|
||||||
|
var receivedInvoicePayments = paymentsTask.Result;
|
||||||
|
|
||||||
|
// Map invoice to response view model
|
||||||
|
var response = _mapper.Map<InvoiceDetailsVM>(invoice);
|
||||||
|
|
||||||
|
// Populate related data
|
||||||
|
if (comments.Any())
|
||||||
|
response.Comments = _mapper.Map<List<InvoiceCommentVM>>(comments);
|
||||||
|
|
||||||
|
if (attachments.Any())
|
||||||
|
{
|
||||||
|
response.Attachments = attachments.Select(a =>
|
||||||
|
{
|
||||||
|
var result = _mapper.Map<InvoiceAttachmentVM>(a);
|
||||||
|
result.PreSignedUrl = _s3Service.GeneratePreSignedUrl(a.Document!.S3Key);
|
||||||
|
result.UploadedBy = _mapper.Map<BasicEmployeeVM>(a.Document.UploadedBy);
|
||||||
|
return result;
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivedInvoicePayments.Any())
|
||||||
|
response.ReceivedInvoicePayments = _mapper.Map<List<ReceivedInvoicePaymentVM>>(receivedInvoicePayments);
|
||||||
|
|
||||||
|
// Compute total paid and balance amounts
|
||||||
|
double totalPaidAmount = receivedInvoicePayments.Sum(rip => rip.Amount);
|
||||||
|
double totalAmount = invoice.BasicAmount + invoice.TaxAmount;
|
||||||
|
response.BalanceAmount = totalAmount - totalPaidAmount;
|
||||||
|
|
||||||
|
_logger.LogInfo("Invoice {InvoiceId} details fetched successfully: Total = {TotalAmount}, Paid = {PaidAmount}, Balance = {BalanceAmount}",
|
||||||
|
id, totalAmount, totalPaidAmount, response.BalanceAmount);
|
||||||
|
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(response, "Invoice details fetched successfully", 200));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("invoice/create")]
|
||||||
|
public async Task<IActionResult> CreateInvoiceAsync([FromBody] InvoiceDto model)
|
||||||
|
{
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
_logger.LogInfo("Starting invoice creation for ProjectId: {ProjectId} by EmployeeId: {EmployeeId}",
|
||||||
|
model.ProjectId, loggedInEmployee.Id);
|
||||||
|
|
||||||
|
if (model.InvoiceNumber.Length > 17)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice Number {InvoiceNumber} is greater than 17 charater",
|
||||||
|
model.InvoiceNumber);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice Number cannot be greater than 17 charater",
|
||||||
|
"Invoice Number is greater than 17 charater", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate date sequence
|
||||||
|
if (model.InvoiceDate.Date > DateTime.UtcNow.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice date {InvoiceDate} cannot be in the future.",
|
||||||
|
model.InvoiceDate);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice date cannot be in the future",
|
||||||
|
"Invoice date cannot be in the future", 400));
|
||||||
|
}
|
||||||
|
if (model.InvoiceDate.Date > model.ClientSubmitedDate.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice date {InvoiceDate} is later than client submitted date {ClientSubmitedDate}",
|
||||||
|
model.InvoiceDate, model.ClientSubmitedDate);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice date cannot be later than the client submitted date",
|
||||||
|
"Invoice date is later than client submitted date", 400));
|
||||||
|
}
|
||||||
|
if (model.ClientSubmitedDate.Date > DateTime.UtcNow.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Client submited date {ClientSubmitedDate} cannot be in the future.",
|
||||||
|
model.InvoiceDate);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Client submited date cannot be in the future",
|
||||||
|
"Client submited date cannot be in the future", 400));
|
||||||
|
}
|
||||||
|
if (model.ClientSubmitedDate.Date > model.ExceptedPaymentDate.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Client submitted date {ClientSubmitedDate} is later than expected payment date {ExpectedPaymentDate}",
|
||||||
|
model.ClientSubmitedDate, model.ExceptedPaymentDate);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Client submission date cannot be later than the expected payment date",
|
||||||
|
"Client submitted date is later than expected payment date", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing InvoiceNumber for this tenant before creating/updating to maintain uniqueness.
|
||||||
|
var invoiceNumberExists = await _context.Invoices
|
||||||
|
.AnyAsync(i => i.InvoiceNumber == model.InvoiceNumber && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (invoiceNumberExists)
|
||||||
|
{
|
||||||
|
// Log the conflict event with full context for audit/review.
|
||||||
|
_logger.LogWarning(
|
||||||
|
"Invoice number conflict detected for InvoiceNumber: {InvoiceNumber} and TenantId: {TenantId}",
|
||||||
|
model.InvoiceNumber, tenantId);
|
||||||
|
|
||||||
|
// Return HTTP 409 (Conflict) with a descriptive, actionable message.
|
||||||
|
return StatusCode(409, ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice number already exists",
|
||||||
|
$"The invoice number '{model.InvoiceNumber}' is already in use for this tenant. Please choose a unique invoice number.",
|
||||||
|
409));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If E-InvoiceNumber is provided (optional), validate its uniqueness for this tenant.
|
||||||
|
if (!string.IsNullOrWhiteSpace(model.EInvoiceNumber))
|
||||||
|
{
|
||||||
|
var eInvoiceNumberExists = await _context.Invoices
|
||||||
|
.AnyAsync(i => i.EInvoiceNumber == model.EInvoiceNumber && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (eInvoiceNumberExists)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"E-Invoice number conflict detected for EInvoiceNumber: {EInvoiceNumber} and TenantId: {TenantId}",
|
||||||
|
model.EInvoiceNumber, tenantId);
|
||||||
|
|
||||||
|
// Return HTTP 409 (Conflict) with a tailored message for E-Invoice.
|
||||||
|
return StatusCode(409, ApiResponse<object>.ErrorResponse(
|
||||||
|
"E-Invoice number already exists",
|
||||||
|
$"The E-Invoice number '{model.EInvoiceNumber}' is already assigned to another invoice for this tenant. Please provide a unique E-Invoice number.",
|
||||||
|
409));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch project
|
||||||
|
var project = await _context.Projects
|
||||||
|
.FirstOrDefaultAsync(p => p.Id == model.ProjectId && p.TenantId == tenantId);
|
||||||
|
if (project == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Project not found: ProjectId {ProjectId}, TenantId {TenantId}",
|
||||||
|
model.ProjectId, tenantId);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin transaction scope with async flow support
|
||||||
|
await using var transaction = await _context.Database.BeginTransactionAsync();
|
||||||
|
var invoice = new Invoice();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Map and create invoice
|
||||||
|
invoice = _mapper.Map<Invoice>(model);
|
||||||
|
invoice.IsActive = true;
|
||||||
|
invoice.MarkAsCompleted = false;
|
||||||
|
invoice.CreatedAt = DateTime.UtcNow;
|
||||||
|
invoice.CreatedById = loggedInEmployee.Id;
|
||||||
|
invoice.TenantId = tenantId;
|
||||||
|
|
||||||
|
_context.Invoices.Add(invoice);
|
||||||
|
await _context.SaveChangesAsync(); // Save to generate invoice.Id
|
||||||
|
|
||||||
|
// Handle attachments
|
||||||
|
var documents = new List<Document>();
|
||||||
|
var invoiceAttachments = new List<InvoiceAttachment>();
|
||||||
|
if (model.Attachments?.Any() == true)
|
||||||
|
{
|
||||||
|
var batchId = Guid.NewGuid();
|
||||||
|
|
||||||
|
foreach (var attachment in model.Attachments)
|
||||||
|
{
|
||||||
|
string base64 = attachment.Base64Data?.Split(',').LastOrDefault() ?? "";
|
||||||
|
if (string.IsNullOrWhiteSpace(base64))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Base64 data is missing for attachment {FileName}", attachment.FileName ?? "");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Image data missing", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileType = _s3Service.GetContentTypeFromBase64(base64);
|
||||||
|
var fileName = _s3Service.GenerateFileName(fileType, tenantId, "invoice");
|
||||||
|
var objectKey = $"tenant-{tenantId}/Project/{model.ProjectId}/Invoice/{fileName}";
|
||||||
|
|
||||||
|
await _s3Service.UploadFileAsync(base64, fileType, objectKey);
|
||||||
|
|
||||||
|
var document = new Document
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
BatchId = batchId,
|
||||||
|
UploadedById = loggedInEmployee.Id,
|
||||||
|
FileName = attachment.FileName ?? fileName,
|
||||||
|
ContentType = attachment.ContentType,
|
||||||
|
S3Key = objectKey,
|
||||||
|
FileSize = attachment.FileSize,
|
||||||
|
UploadedAt = DateTime.UtcNow,
|
||||||
|
TenantId = tenantId
|
||||||
|
};
|
||||||
|
documents.Add(document);
|
||||||
|
|
||||||
|
var invoiceAttachment = new InvoiceAttachment
|
||||||
|
{
|
||||||
|
InvoiceId = invoice.Id,
|
||||||
|
DocumentId = document.Id,
|
||||||
|
TenantId = tenantId
|
||||||
|
};
|
||||||
|
invoiceAttachments.Add(invoiceAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Documents.AddRange(documents);
|
||||||
|
_context.InvoiceAttachments.AddRange(invoiceAttachments);
|
||||||
|
await _context.SaveChangesAsync(); // Save attachments and mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit transaction
|
||||||
|
await transaction.CommitAsync();
|
||||||
|
_logger.LogInfo("Invoice {InvoiceId} created successfully with {AttachmentCount} attachments.",
|
||||||
|
invoice.Id, documents.Count);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await transaction.RollbackAsync();
|
||||||
|
_logger.LogError(ex, "Transaction rolled back during invoice creation for ProjectId {ProjectId}", model.ProjectId);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse(
|
||||||
|
"Transaction failed: " + ex.Message,
|
||||||
|
"An error occurred while creating the invoice", 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build response
|
||||||
|
var response = _mapper.Map<InvoiceListVM>(invoice);
|
||||||
|
response.Project = _mapper.Map<BasicProjectVM>(project);
|
||||||
|
response.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
|
||||||
|
response.BalanceAmount = response.BasicAmount + response.TaxAmount;
|
||||||
|
|
||||||
|
return StatusCode(201, ApiResponse<object>.SuccessResponse(response, "Invoice Created Successfully", 201));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new received invoice payment record after validating business rules.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The received invoice payment data transfer object containing payment details.</param>
|
||||||
|
/// <returns>An action result containing the created payment view model or error response.</returns>
|
||||||
|
|
||||||
|
[HttpPost("invoice/payment/received")]
|
||||||
|
public async Task<IActionResult> CreateReceivedInvoicePaymentAsync([FromBody] ReceivedInvoicePaymentDto model)
|
||||||
|
{
|
||||||
|
// Validate input model
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Received invoice payment creation request with null model");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid model", "Request payload cannot be null", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
// Retrieve invoice with tenant isolation and no tracking for read-only access
|
||||||
|
var invoice = await _context.Invoices
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(i => i.Id == model.InvoiceId && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (invoice == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice not found for ID {InvoiceId} and TenantId {TenantId}", model.InvoiceId, tenantId);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Invoice not found", "The specified invoice does not exist", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if invoice is already marked as completed
|
||||||
|
if (invoice.MarkAsCompleted)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Attempt to add payment to completed invoice {InvoiceId}", model.InvoiceId);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Cannot add received payment to completed invoice",
|
||||||
|
"Payments cannot be added to invoices that are already marked as completed", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate payment received date is not in the future
|
||||||
|
if (model.PaymentReceivedDate.Date > DateTime.UtcNow.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Future payment date {PaymentReceivedDate} provided for invoice {InvoiceId}",
|
||||||
|
model.PaymentReceivedDate, model.InvoiceId);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Payment received date cannot be in the future",
|
||||||
|
"The payment received date must not be later than the current date", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate client submitted date is not later than payment received date
|
||||||
|
if (invoice.ClientSubmitedDate.Date > model.PaymentReceivedDate.Date)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Client submitted date {ClientSubmitedDate} is later than payment received date {PaymentReceivedDate} for invoice {InvoiceId}",
|
||||||
|
invoice.ClientSubmitedDate, model.PaymentReceivedDate, model.InvoiceId);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Client submission date cannot be later than the payment received date",
|
||||||
|
"The client submission date cannot be later than the payment received date", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve all previous payments for the given invoice and tenant in a single, efficient query.
|
||||||
|
var receivedInvoicePayments = await _context.ReceivedInvoicePayments
|
||||||
|
.Where(rip => rip.InvoiceId == invoice.Id && rip.TenantId == tenantId)
|
||||||
|
.Select(rip => rip.Amount) // Only select required field for better performance.
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Calculate the sum of all previous payments to determine the total paid so far.
|
||||||
|
var previousPaidAmount = receivedInvoicePayments.Sum();
|
||||||
|
var totalPaidAmount = previousPaidAmount + model.Amount;
|
||||||
|
|
||||||
|
// Compute the invoice's total amount payable including taxes.
|
||||||
|
var totalAmount = invoice.BasicAmount + invoice.TaxAmount;
|
||||||
|
|
||||||
|
// Business rule validation: Prevent the overpayment scenario.
|
||||||
|
if (totalPaidAmount > totalAmount)
|
||||||
|
{
|
||||||
|
// Log the details for easier debugging and audit trails.
|
||||||
|
_logger.LogWarning(
|
||||||
|
"Overpayment attempt detected. InvoiceId: {InvoiceId}, TenantId: {TenantId}, TotalInvoiceAmount: {TotalInvoiceAmount}, PreviousPaidAmount: {PreviousPaidAmount}, AttemptedPayment: {AttemptedPayment}, CalculatedTotalPaid: {TotalPaidAmount}.",
|
||||||
|
invoice.Id, tenantId, totalAmount, previousPaidAmount, model.Amount, totalPaidAmount);
|
||||||
|
|
||||||
|
// Return a bad request response with a clear, actionable error message.
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invalid payment: total paid amount exceeds invoice total.",
|
||||||
|
$"The total of existing payments ({previousPaidAmount}) plus the new payment ({model.Amount}) would exceed the invoice total ({totalAmount}). Please verify payment details.",
|
||||||
|
400));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Map DTO to entity and set creation metadata
|
||||||
|
var receivedInvoicePayment = _mapper.Map<ReceivedInvoicePayment>(model);
|
||||||
|
receivedInvoicePayment.CreatedAt = DateTime.UtcNow;
|
||||||
|
receivedInvoicePayment.CreatedById = loggedInEmployee.Id;
|
||||||
|
receivedInvoicePayment.TenantId = tenantId;
|
||||||
|
|
||||||
|
// Add new payment record and save changes
|
||||||
|
_context.ReceivedInvoicePayments.Add(receivedInvoicePayment);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Map entity to view model for response
|
||||||
|
var response = _mapper.Map<ReceivedInvoicePaymentVM>(receivedInvoicePayment);
|
||||||
|
|
||||||
|
_logger.LogInfo("Successfully created received payment {PaymentId} for invoice {InvoiceId}",
|
||||||
|
receivedInvoicePayment.Id, model.InvoiceId);
|
||||||
|
|
||||||
|
return StatusCode(201, ApiResponse<object>.SuccessResponse(response, "Payment invoice received successfully", 201));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occurred while creating received payment for invoice {InvoiceId}", model.InvoiceId);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse(
|
||||||
|
"Internal server error",
|
||||||
|
"An unexpected error occurred while processing the request", 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a comment to the specified invoice, validating model and invoice existence.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">DTO containing InvoiceId and Comment text.</param>
|
||||||
|
/// <returns>201 Created with comment details, or error codes for validation/invoice not found.</returns>
|
||||||
|
[HttpPost("invoice/add/comment")]
|
||||||
|
public async Task<IActionResult> AddCommentToInvoiceAsync([FromBody] InvoiceCommentDto model)
|
||||||
|
{
|
||||||
|
// Validate incoming data early to avoid unnecessary database calls.
|
||||||
|
if (string.IsNullOrWhiteSpace(model.Comment))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invalid or missing comment data for InvoiceId {InvoiceId}, TenantId {TenantId}", model.InvoiceId, tenantId);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invalid comment data",
|
||||||
|
"The comment text and model must not be null or empty.",
|
||||||
|
400));
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
// Find the target invoice for the specified tenant.
|
||||||
|
var invoice = await _context.Invoices
|
||||||
|
.FirstOrDefaultAsync(i => i.Id == model.InvoiceId && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (invoice == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cannot add comment—invoice not found. InvoiceId {InvoiceId}, TenantId {TenantId}", model.InvoiceId, tenantId);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice not found",
|
||||||
|
$"Invoice with ID '{model.InvoiceId}' does not exist for the specified tenant.",
|
||||||
|
404));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the new comment entity with required audit metadata.
|
||||||
|
var comment = new InvoiceComment
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Comment = model.Comment.Trim(),
|
||||||
|
InvoiceId = model.InvoiceId,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
CreatedById = loggedInEmployee.Id,
|
||||||
|
TenantId = tenantId
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.InvoiceComments.Add(comment);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInfo("Added new comment to invoice {InvoiceId} by employee {EmployeeId}, TenantId {TenantId}",
|
||||||
|
comment.InvoiceId, loggedInEmployee.Id, tenantId);
|
||||||
|
|
||||||
|
var response = _mapper.Map<InvoiceCommentVM>(comment);
|
||||||
|
|
||||||
|
// Return successful creation with comment details.
|
||||||
|
return StatusCode(201, ApiResponse<object>.SuccessResponse(
|
||||||
|
response,
|
||||||
|
"Comment added to invoice successfully.",
|
||||||
|
201));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks the specified invoice as completed if it exists and is not already completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invoiceId">The unique identifier of the invoice to mark as completed.</param>
|
||||||
|
/// <returns>An action result indicating success or the nature of the error.</returns>
|
||||||
|
|
||||||
|
[HttpPut("invoice/marked/completed/{invoiceId}")]
|
||||||
|
public async Task<IActionResult> MarkAsCompletedAsync(Guid invoiceId)
|
||||||
|
{
|
||||||
|
// Create a new async database context for the current request's scope.
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
// Retrieve the current logged in employee for audit/logging (optional use).
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
|
// Attempt to find the invoice with tenant isolation; use AsNoTracking if no updates needed (but here we update so tracking is okay).
|
||||||
|
var invoice = await _context.Invoices
|
||||||
|
.FirstOrDefaultAsync(i => i.Id == invoiceId && i.TenantId == tenantId);
|
||||||
|
|
||||||
|
// Log and return 404 if the invoice does not exist.
|
||||||
|
if (invoice == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invoice not found for ID {InvoiceId} and TenantId {TenantId}", invoiceId, tenantId);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Invoice not found", "The specified invoice does not exist", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the invoice is already marked as completed, log and return meaningful error.
|
||||||
|
if (invoice.MarkAsCompleted)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Attempt to mark already completed invoice {InvoiceId} as completed by user {UserId}", invoiceId, loggedInEmployee.Id);
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse(
|
||||||
|
"Invoice already completed",
|
||||||
|
"Invoice is already marked as completed", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Mark invoice as completed.
|
||||||
|
invoice.MarkAsCompleted = true;
|
||||||
|
|
||||||
|
// Persist the change to the database.
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInfo("Invoice {InvoiceId} marked as completed by user {UserId}", invoiceId, loggedInEmployee.Id);
|
||||||
|
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(new { }, "Invoice is marked as completed successfully", 200));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occurred while marking invoice {InvoiceId} as completed by user {UserId}", invoiceId, loggedInEmployee.Id);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse(
|
||||||
|
"Internal server error",
|
||||||
|
"An unexpected error occurred while processing the request", 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads invoice comments asynchronously with related metadata.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<InvoiceComment>> LoadInvoiceCommentsAsync(Guid invoiceId, Guid tenantId)
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.InvoiceComments
|
||||||
|
.Include(ic => ic.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(ic => ic.InvoiceId == invoiceId && ic.TenantId == tenantId)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads invoice attachments and their upload metadata asynchronously.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<InvoiceAttachment>> LoadInvoiceAttachmentsAsync(Guid invoiceId, Guid tenantId)
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.InvoiceAttachments
|
||||||
|
.Include(ia => ia.Document)
|
||||||
|
.ThenInclude(d => d!.UploadedBy)
|
||||||
|
.ThenInclude(e => e!.JobRole)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(ia => ia.InvoiceId == invoiceId && ia.TenantId == tenantId && ia.Document != null && ia.Document.UploadedBy != null)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads received invoice payment records asynchronously with creator metadata.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<ReceivedInvoicePayment>> LoadReceivedInvoicePaymentsAsync(Guid invoiceId, Guid tenantId)
|
||||||
|
{
|
||||||
|
await using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await context.ReceivedInvoicePayments
|
||||||
|
.Include(rip => rip.CreatedBy).ThenInclude(e => e!.JobRole)
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(rip => rip.InvoiceId == invoiceId && rip.TenantId == tenantId)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ namespace MarcoBMS.Services.Helpers
|
|||||||
public async Task<Employee> GetEmployeeByID(Guid EmployeeID)
|
public async Task<Employee> GetEmployeeByID(Guid EmployeeID)
|
||||||
{
|
{
|
||||||
|
|
||||||
return await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { };
|
return await _context.Employees.Include(e => e.JobRole).Include(e => e.Organization).FirstOrDefaultAsync(e => e.Id == EmployeeID) ?? new Employee { };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID)
|
public async Task<Employee> GetEmployeeByApplicationUserID(string ApplicationUserID)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Marco.Pms.Model.AppMenu;
|
using Marco.Pms.Model.AppMenu;
|
||||||
|
using Marco.Pms.Model.Collection;
|
||||||
using Marco.Pms.Model.Directory;
|
using Marco.Pms.Model.Directory;
|
||||||
using Marco.Pms.Model.DocumentManager;
|
using Marco.Pms.Model.DocumentManager;
|
||||||
using Marco.Pms.Model.Dtos.Activities;
|
using Marco.Pms.Model.Dtos.Activities;
|
||||||
using Marco.Pms.Model.Dtos.AppMenu;
|
using Marco.Pms.Model.Dtos.AppMenu;
|
||||||
|
using Marco.Pms.Model.Dtos.Collection;
|
||||||
using Marco.Pms.Model.Dtos.Directory;
|
using Marco.Pms.Model.Dtos.Directory;
|
||||||
using Marco.Pms.Model.Dtos.DocumentManager;
|
using Marco.Pms.Model.Dtos.DocumentManager;
|
||||||
using Marco.Pms.Model.Dtos.Employees;
|
using Marco.Pms.Model.Dtos.Employees;
|
||||||
@ -26,6 +28,7 @@ using Marco.Pms.Model.Projects;
|
|||||||
using Marco.Pms.Model.TenantModels;
|
using Marco.Pms.Model.TenantModels;
|
||||||
using Marco.Pms.Model.TenantModels.MongoDBModel;
|
using Marco.Pms.Model.TenantModels.MongoDBModel;
|
||||||
using Marco.Pms.Model.ViewModels.Activities;
|
using Marco.Pms.Model.ViewModels.Activities;
|
||||||
|
using Marco.Pms.Model.ViewModels.Collection;
|
||||||
using Marco.Pms.Model.ViewModels.Directory;
|
using Marco.Pms.Model.ViewModels.Directory;
|
||||||
using Marco.Pms.Model.ViewModels.DocumentManager;
|
using Marco.Pms.Model.ViewModels.DocumentManager;
|
||||||
using Marco.Pms.Model.ViewModels.Employee;
|
using Marco.Pms.Model.ViewModels.Employee;
|
||||||
@ -255,6 +258,19 @@ namespace Marco.Pms.Services.MappingProfiles
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region ======================================================= Collection =======================================================
|
||||||
|
CreateMap<InvoiceDto, Invoice>();
|
||||||
|
CreateMap<Invoice, InvoiceListVM>();
|
||||||
|
CreateMap<Invoice, InvoiceDetailsVM>();
|
||||||
|
|
||||||
|
CreateMap<ReceivedInvoicePaymentDto, ReceivedInvoicePayment>();
|
||||||
|
CreateMap<ReceivedInvoicePayment, ReceivedInvoicePaymentVM>();
|
||||||
|
|
||||||
|
CreateMap<InvoiceComment, InvoiceCommentVM>();
|
||||||
|
|
||||||
|
CreateMap<InvoiceAttachment, InvoiceAttachmentVM>();
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region ======================================================= Master =======================================================
|
#region ======================================================= Master =======================================================
|
||||||
|
|
||||||
CreateMap<FeaturePermission, FeaturePermissionVM>();
|
CreateMap<FeaturePermission, FeaturePermissionVM>();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user