2016-11-06 02:03:36 +01:00
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
|
using System.ComponentModel.DataAnnotations.Schema;
|
|
|
|
|
|
2016-06-14 03:37:57 +02:00
|
|
|
namespace Yavsc.Models.Billing
|
2016-05-17 18:22:18 +02:00
|
|
|
{
|
2017-02-11 15:46:26 +01:00
|
|
|
using Models.Workflow;
|
2017-02-22 22:58:02 +01:00
|
|
|
using Newtonsoft.Json;
|
2017-01-13 16:31:40 +01:00
|
|
|
|
2016-10-07 12:49:24 +02:00
|
|
|
public partial class Estimate : IEstimate
|
2016-05-17 18:22:18 +02:00
|
|
|
{
|
|
|
|
|
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
|
|
|
public long Id { get; set; }
|
|
|
|
|
|
|
|
|
|
public long? CommandId { get; set; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// A command is not required to create
|
|
|
|
|
/// an estimate,
|
|
|
|
|
/// it will result in a new estimate template
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2017-02-22 22:58:02 +01:00
|
|
|
[ForeignKey("CommandId"),JsonIgnore]
|
2017-02-27 19:28:32 +01:00
|
|
|
public RdvQuery Query { get; set; }
|
2016-05-17 18:22:18 +02:00
|
|
|
public string Description { get; set; }
|
|
|
|
|
public string Title { get; set; }
|
2016-10-23 01:31:09 +02:00
|
|
|
|
|
|
|
|
[InverseProperty("Estimate")]
|
|
|
|
|
public virtual List<CommandLine> Bill { get; set; }
|
2016-05-17 18:22:18 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// List of attached graphic files
|
|
|
|
|
/// to this estimate, as relative pathes to
|
|
|
|
|
/// the command performer's root path.
|
|
|
|
|
/// In db, they are separated by <c>:</c>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[NotMapped]
|
|
|
|
|
public List<string> AttachedGraphics { get; set; }
|
|
|
|
|
|
2016-10-07 12:49:24 +02:00
|
|
|
public string AttachedGraphicsString
|
|
|
|
|
{
|
2016-08-13 01:26:32 +02:00
|
|
|
get { return string.Join(":", AttachedGraphics); }
|
2016-10-07 12:49:24 +02:00
|
|
|
set { AttachedGraphics = value.Split(':').ToList(); }
|
2016-08-13 01:26:32 +02:00
|
|
|
}
|
2016-05-17 18:22:18 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// List of attached files
|
|
|
|
|
/// to this estimate, as relative pathes to
|
|
|
|
|
/// the command performer's root path.
|
|
|
|
|
/// In db, they are separated by <c>:</c>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[NotMapped]
|
|
|
|
|
public List<string> AttachedFiles { get; set; }
|
2016-10-07 12:49:24 +02:00
|
|
|
public string AttachedFilesString
|
|
|
|
|
{
|
2016-08-13 01:26:32 +02:00
|
|
|
get { return string.Join(":", AttachedFiles); }
|
2016-10-07 12:49:24 +02:00
|
|
|
set { AttachedFiles = value.Split(':').ToList(); }
|
2016-08-13 01:26:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string OwnerId { get; set; }
|
|
|
|
|
|
2017-02-22 22:58:02 +01:00
|
|
|
[ForeignKey("OwnerId"),JsonIgnore]
|
feat(api): POST /api/bill/estimate/{id}/sign — JSON signature capture
Adds a new JSON-bodied signature endpoint as a sibling of the
legacy PNG-based prosign/clisign routes. The legacy flow stays
intact: the TeX invoice templates (Bill_tex.cshtml,
Estimate_tex.cshtml) still consume the sign-{billingCode}-{id}.png
files the old endpoints write, and the new endpoint writes to a
distinct /signatures/ tree under UserFilesDirName. A future
migration commit will regenerate PNGs from the JSON payload and
decommission the PNG flow.
Scope
- New Signature entity (Yavsc.Server/Models/Billing/Signature.cs)
with FK to Estimate, FK to ApplicationUser (Signer), Type
(Pro/Client) enum, CoordinateMax (default 10_000), int[] Strokes
(native Npgsql mapping), CapturedAtUtc, FilePath. Multiple
versions per (EstimateId, Type) are allowed; the controller
reads the most recent.
- New Estimate.Signatures nav collection (InverseProperty) so the
composite index covers both sides of the relation.
- New DbSet<Signature> Signatures + composite index
(EstimateId, Type, CapturedAtUtc DESC) in ApplicationDbContext
OnModelCreating. DeleteBehavior.Cascade on Estimate deletion
cleans up signatures automatically.
- New EstimateSignatureFileHelper (Server/Helpers) with
ReceiveEstimateSignatureAsync(user, estimateId, type, payload).
Writes a yavsc.signature/v1 JSON envelope to
UserFilesDirName/{user}/signatures/sign-{type}-{estimateId}-{ticks}.json.
Quota update lives in the controller, not the helper, because
the helper has no DbContext access.
- New endpoint POST /api/bill/estimate/{id:long}/sign on
BillingController. Authz is body-driven (the bearer token is the
PostIt OAuth client, not the end user, so signerUserId is in
the JSON body, validated against Estimate.OwnerId/ClientId).
Returns 201 Created with the new Signature's metadata.
Plumbing
- SignatureSubmission (body type) lives next to BillingController
in the same file — small enough to keep colocated.
- The legacy prosign/clisign routes are untouched. They keep
the IFormFile PNG contract; the new endpoint is the JSON
counterpart.
Tests
- New EstimateSignatureFileHelperTests in Yavsc.Org.Tests
(8 tests, all green): filename format incl. lowercase type and
ticks, envelope v1 round-trip (parsed via JsonDocument, not
text matching), null payload rejected, non-positive
estimateId rejected. Disk side effects are isolated to a
per-test temp root via AbstractFileSystemHelpers.UserFilesDirName.
- Yavsc.Org.Tests full suite: 29/29 green.
- PostIt.Tests: 57/57 green (untouched by this commit).
- Builds: Yavsc.Server, Yavsc.Api, Yavsc.Org, Yavsc.Org.Tests
all compile clean.
Out of scope
- EF migration: the Signatures table doesn't exist in the
database yet. The migration is intentionally a separate
commit so the generated SQL can be reviewed against the
composite index and the int[] column type before it touches
any prod database. Until the migration lands, the new
endpoint will 500 on SaveChanges; the [DEV] button in
PostIt is the only call site, so this is acceptable.
- SignalR handler that opens the signature page on a
'devis received' push — commit 4.
2026-07-04 15:47:55 +01:00
|
|
|
public virtual PerformerProfile Owner { get; set; }
|
2017-01-13 16:31:40 +01:00
|
|
|
|
2016-08-13 01:26:32 +02:00
|
|
|
[Required]
|
|
|
|
|
public string ClientId { get; set; }
|
2017-02-22 22:58:02 +01:00
|
|
|
[ForeignKey("ClientId"),JsonIgnore]
|
feat(api): POST /api/bill/estimate/{id}/sign — JSON signature capture
Adds a new JSON-bodied signature endpoint as a sibling of the
legacy PNG-based prosign/clisign routes. The legacy flow stays
intact: the TeX invoice templates (Bill_tex.cshtml,
Estimate_tex.cshtml) still consume the sign-{billingCode}-{id}.png
files the old endpoints write, and the new endpoint writes to a
distinct /signatures/ tree under UserFilesDirName. A future
migration commit will regenerate PNGs from the JSON payload and
decommission the PNG flow.
Scope
- New Signature entity (Yavsc.Server/Models/Billing/Signature.cs)
with FK to Estimate, FK to ApplicationUser (Signer), Type
(Pro/Client) enum, CoordinateMax (default 10_000), int[] Strokes
(native Npgsql mapping), CapturedAtUtc, FilePath. Multiple
versions per (EstimateId, Type) are allowed; the controller
reads the most recent.
- New Estimate.Signatures nav collection (InverseProperty) so the
composite index covers both sides of the relation.
- New DbSet<Signature> Signatures + composite index
(EstimateId, Type, CapturedAtUtc DESC) in ApplicationDbContext
OnModelCreating. DeleteBehavior.Cascade on Estimate deletion
cleans up signatures automatically.
- New EstimateSignatureFileHelper (Server/Helpers) with
ReceiveEstimateSignatureAsync(user, estimateId, type, payload).
Writes a yavsc.signature/v1 JSON envelope to
UserFilesDirName/{user}/signatures/sign-{type}-{estimateId}-{ticks}.json.
Quota update lives in the controller, not the helper, because
the helper has no DbContext access.
- New endpoint POST /api/bill/estimate/{id:long}/sign on
BillingController. Authz is body-driven (the bearer token is the
PostIt OAuth client, not the end user, so signerUserId is in
the JSON body, validated against Estimate.OwnerId/ClientId).
Returns 201 Created with the new Signature's metadata.
Plumbing
- SignatureSubmission (body type) lives next to BillingController
in the same file — small enough to keep colocated.
- The legacy prosign/clisign routes are untouched. They keep
the IFormFile PNG contract; the new endpoint is the JSON
counterpart.
Tests
- New EstimateSignatureFileHelperTests in Yavsc.Org.Tests
(8 tests, all green): filename format incl. lowercase type and
ticks, envelope v1 round-trip (parsed via JsonDocument, not
text matching), null payload rejected, non-positive
estimateId rejected. Disk side effects are isolated to a
per-test temp root via AbstractFileSystemHelpers.UserFilesDirName.
- Yavsc.Org.Tests full suite: 29/29 green.
- PostIt.Tests: 57/57 green (untouched by this commit).
- Builds: Yavsc.Server, Yavsc.Api, Yavsc.Org, Yavsc.Org.Tests
all compile clean.
Out of scope
- EF migration: the Signatures table doesn't exist in the
database yet. The migration is intentionally a separate
commit so the generated SQL can be reviewed against the
composite index and the int[] column type before it touches
any prod database. Until the migration lands, the new
endpoint will 500 on SaveChanges; the [DEV] button in
PostIt is the only call site, so this is acceptable.
- SignalR handler that opens the signature page on a
'devis received' push — commit 4.
2026-07-04 15:47:55 +01:00
|
|
|
public virtual ApplicationUser Client { get; set; }
|
2016-10-07 12:49:24 +02:00
|
|
|
|
2017-03-03 01:17:00 +01:00
|
|
|
[Required]
|
2016-10-07 12:49:24 +02:00
|
|
|
public string CommandType
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
feat(api): POST /api/bill/estimate/{id}/sign — JSON signature capture
Adds a new JSON-bodied signature endpoint as a sibling of the
legacy PNG-based prosign/clisign routes. The legacy flow stays
intact: the TeX invoice templates (Bill_tex.cshtml,
Estimate_tex.cshtml) still consume the sign-{billingCode}-{id}.png
files the old endpoints write, and the new endpoint writes to a
distinct /signatures/ tree under UserFilesDirName. A future
migration commit will regenerate PNGs from the JSON payload and
decommission the PNG flow.
Scope
- New Signature entity (Yavsc.Server/Models/Billing/Signature.cs)
with FK to Estimate, FK to ApplicationUser (Signer), Type
(Pro/Client) enum, CoordinateMax (default 10_000), int[] Strokes
(native Npgsql mapping), CapturedAtUtc, FilePath. Multiple
versions per (EstimateId, Type) are allowed; the controller
reads the most recent.
- New Estimate.Signatures nav collection (InverseProperty) so the
composite index covers both sides of the relation.
- New DbSet<Signature> Signatures + composite index
(EstimateId, Type, CapturedAtUtc DESC) in ApplicationDbContext
OnModelCreating. DeleteBehavior.Cascade on Estimate deletion
cleans up signatures automatically.
- New EstimateSignatureFileHelper (Server/Helpers) with
ReceiveEstimateSignatureAsync(user, estimateId, type, payload).
Writes a yavsc.signature/v1 JSON envelope to
UserFilesDirName/{user}/signatures/sign-{type}-{estimateId}-{ticks}.json.
Quota update lives in the controller, not the helper, because
the helper has no DbContext access.
- New endpoint POST /api/bill/estimate/{id:long}/sign on
BillingController. Authz is body-driven (the bearer token is the
PostIt OAuth client, not the end user, so signerUserId is in
the JSON body, validated against Estimate.OwnerId/ClientId).
Returns 201 Created with the new Signature's metadata.
Plumbing
- SignatureSubmission (body type) lives next to BillingController
in the same file — small enough to keep colocated.
- The legacy prosign/clisign routes are untouched. They keep
the IFormFile PNG contract; the new endpoint is the JSON
counterpart.
Tests
- New EstimateSignatureFileHelperTests in Yavsc.Org.Tests
(8 tests, all green): filename format incl. lowercase type and
ticks, envelope v1 round-trip (parsed via JsonDocument, not
text matching), null payload rejected, non-positive
estimateId rejected. Disk side effects are isolated to a
per-test temp root via AbstractFileSystemHelpers.UserFilesDirName.
- Yavsc.Org.Tests full suite: 29/29 green.
- PostIt.Tests: 57/57 green (untouched by this commit).
- Builds: Yavsc.Server, Yavsc.Api, Yavsc.Org, Yavsc.Org.Tests
all compile clean.
Out of scope
- EF migration: the Signatures table doesn't exist in the
database yet. The migration is intentionally a separate
commit so the generated SQL can be reviewed against the
composite index and the int[] column type before it touches
any prod database. Until the migration lands, the new
endpoint will 500 on SaveChanges; the [DEV] button in
PostIt is the only call site, so this is acceptable.
- SignalR handler that opens the signature page on a
'devis received' push — commit 4.
2026-07-04 15:47:55 +01:00
|
|
|
public DateTime ProviderValidationDate { get; set; }
|
|
|
|
|
public DateTime ClientValidationDate { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// All signatures captured against this estimate, in
|
|
|
|
|
/// capture order. Multiple versions per (Type, SignerId)
|
|
|
|
|
/// are allowed; the controller reads the most recent
|
|
|
|
|
/// when asked. See <see cref="Signature"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[InverseProperty(nameof(Signature.Estimate))]
|
|
|
|
|
public virtual ICollection<Signature> Signatures { get; set; }
|
|
|
|
|
= new List<Signature>();
|
2016-11-24 01:01:41 +01:00
|
|
|
|
2016-05-17 18:22:18 +02:00
|
|
|
}
|
|
|
|
|
}
|