Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/JoinRpg.DataModel/Claim.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using JoinRpg.DataModel.Finances;
using JoinRpg.Helpers;
Expand All @@ -7,7 +8,7 @@
namespace JoinRpg.DataModel;

// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global used by LINQ
public class Claim : IProjectEntity, ILinkable, IFieldContainter
public class Claim : IProjectEntity, ILinkable, IFieldContainter, IValidatableObject
{
public int ClaimId { get; set; }
public int CharacterId { get; set; }
Expand Down Expand Up @@ -81,11 +82,32 @@ public class Claim : IProjectEntity, ILinkable, IFieldContainter

#region Finance

/// <summary>
/// The date when necessary amout to stop increasing of the total price has been paid.
/// </summary>
public DateTime? FixedPaymentDate { get; set; }

/// <summary>
/// Discounted price to be paid for this claim.
/// When set, total price will not be automatically recalculated depending on the current date.
/// Mutually exclusive with <see cref="DiscountPercent"/>.
/// </summary>
[Range(0, int.MaxValue)]
public int? DiscountedPrice { get; set; }

/// <summary>
/// Discount percent for this claim. Do not affect the total price recalculation depending on the current date.
/// Mutually exclusive with <see cref="DiscountedPrice"/>.
/// </summary>
[Range(0, 100)]
public int? DiscountPercent { get; set; }

/// <summary>
/// Fee to pay by player, manually set by master.
/// If null (default), actual fee will be automatically selected
/// from the project's list of payments.
/// </summary>
[Obsolete("Use the DiscountedPrice or DiscountPercent instead")]
public int? CurrentFee { get; set; }

/// <summary>
Expand All @@ -104,6 +126,7 @@ public IEnumerable<FinanceOperation> ApprovedFinanceOperations
/// </summary>
public virtual ICollection<RecurrentPayment> RecurrentPayments { get; set; }

[Obsolete("Set the discount directly")]
public bool PreferentialFeeUser { get; set; }

/// <summary>
Expand Down Expand Up @@ -139,4 +162,14 @@ public IEnumerable<FinanceOperation> ApprovedFinanceOperations

#endregion

/// <inheritdoc />
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (DiscountedPrice.HasValue && DiscountPercent.HasValue)
{
yield return new ValidationResult(
"Only one type of discount could be set",
[nameof(DiscountedPrice), nameof(DiscountPercent)]);
}
}
}
33 changes: 33 additions & 0 deletions src/JoinRpg.DataModel/Finances/PriceValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
using JoinRpg.PrimitiveTypes.ProjectMetadata;

namespace JoinRpg.DataModel.Finances;

public class PriceValue
{
/// <summary>
/// Database Id of a project.
/// </summary>
public int ProjectId { get; set; }

/// <summary>
/// Database Id of a date-time column.
/// </summary>
public int ProjectFeeSettingId { get; set; }

/// <summary>
/// Database Id of a field.
/// </summary>
public int FieldId { get; set; }

/// <summary>
/// Database Id of a field value (only for <see cref="ProjectFieldType.Dropdown"/> and <see cref="ProjectFieldType.MultiSelect"/>).
/// </summary>
public int? FieldValueId { get; set; }

/// <summary>
/// Price value (only positive values are allowed).
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "Fee should be positive.")]
public int Price { get; set; }
}
7 changes: 7 additions & 0 deletions src/JoinRpg.DataModel/Finances/ProjectFeeSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ namespace JoinRpg.DataModel;
public class ProjectFeeSetting
{
public int ProjectFeeSettingId { get; set; }

public int ProjectId { get; set; }

public virtual Project Project { get; set; }

[Obsolete("Date-dependent prices are now configured using PriveValue")]
[Range(0, int.MaxValue, ErrorMessage = "Fee should be positive.")]
public int Fee { get; set; }

[Obsolete("Date-dependent prices are now configured using PriveValue")]
[Range(0, int.MaxValue, ErrorMessage = "Fee should be positive.")]
public int? PreferentialFee { get; set; }

public DateTime StartDate { get; set; }
}
22 changes: 22 additions & 0 deletions src/JoinRpg.DataModel/Finances/ReceiptItemType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace JoinRpg.DataModel.Finances;

/// <summary>
/// Describes how a field with price should be treated when issuing a receipt.
/// </summary>
public enum ReceiptItemType
{
/// <summary>
/// Field should not have its own receipt position.
/// </summary>
IncludeIntoPrimary,

/// <summary>
/// Field represents a certain commodity.
/// </summary>
Commodity,

/// <summary>
/// Field represents a certain service.
/// </summary>
Service,
}
55 changes: 48 additions & 7 deletions src/JoinRpg.DataModel/ProjectField.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using JoinRpg.DataModel.Finances;
using JoinRpg.Helpers;
using JoinRpg.PrimitiveTypes.ProjectMetadata;

Expand Down Expand Up @@ -44,16 +45,56 @@ public class ProjectField : IProjectEntity, IDeletableSubEntity, IValidatableObj
public bool ShowOnUnApprovedClaims { get; set; }

/// <summary>
/// Price associated with current field.
/// Will be used in payment calculations.
/// Value usage differs on FieldType. Value will be:
/// - Number: multiplied with entered number
/// - CheckBox: used if checkbox was checked
/// - Dropdown, Multiselect: ignored, see values for prices
/// - String, Text, Header: ignored
/// When true, this field may have assigned prices.
/// </summary>
/// <remarks>
/// Has effect only when field has certain <see cref="FieldType"/>:
/// <ul>
/// <li><see cref="ProjectFieldType.Checkbox"/></li>
/// <li><see cref="ProjectFieldType.Number"/></li>
/// <li><see cref="ProjectFieldType.Dropdown"/></li>
/// <li><see cref="ProjectFieldType.MultiSelect" /></li>
/// </ul>
/// </remarks>
public bool Payable { get; set; }

/// <summary>
/// Base price associated with this field.
/// </summary>
/// <remarks>
/// Actual price depends on current date via <see cref="PriceValue"/> objects.
/// </remarks>
public int Price { get; set; }

/// <summary>
/// Defines how this field should be interpreted when preparing a receipt.
/// </summary>
public ReceiptItemType ReceiptItemType { get; set; }

/// <summary>
/// The name used to display in receipt. When not specified, the <see cref="FieldName"/> is used.
/// </summary>
/// <remarks>
/// When <see cref="FieldType"/> is <see cref="ProjectFieldType.Dropdown"/> or <see cref="ProjectFieldType.MultiSelect"/>
/// this value has to be specified independently in each <see cref="ProjectFieldDropdownValue"/>.
/// </remarks>
[StringLength(64)]
public string? ReceiptName { get; set; }

/// <summary>
/// When true, a discount can be applied to price of this field.
/// </summary>
/// <remarks>
/// Whereas the relative discount set in <see cref="Claim.DiscountPercent"/> will only affect the total sum while calculation,
/// the <see cref="Claim.DiscountedPrice"/> will exclude discountable field from total calculation.
/// </remarks>
public bool Discountable { get; set; }

/// <summary>
/// Collection of related price values.
/// </summary>
public ICollection<PriceValue>? PriceValues { get; set; }

bool IDeletableSubEntity.CanBePermanentlyDeleted => !WasEverUsed;

public virtual ICollection<ProjectFieldDropdownValue> DropdownValues { get; set; } =
Expand Down
23 changes: 21 additions & 2 deletions src/JoinRpg.DataModel/ProjectFieldDropdownValue.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using JoinRpg.DataModel.Finances;
using JoinRpg.Helpers;

namespace JoinRpg.DataModel;
Expand Down Expand Up @@ -32,17 +33,35 @@ public class ProjectFieldDropdownValue : IDeletableSubEntity, IProjectEntity, IV

public MarkdownString MasterDescription { get; set; }

#region Finance

/// <summary>
/// Price associated with this value.
/// Base price associated with this field value.
/// </summary>
/// <remarks>
/// Actual price depends on current date via <see cref="PriceValue"/> objects.
/// </remarks>
public int Price { get; set; }

/// <summary>
/// The name used to display in receipt.
/// When not specified, the <see cref="Label"/> property value will be taken but truncated to 64 characters.
/// </summary>
[StringLength(64)]
public string? ReceiptName { get; set; }

/// <summary>
/// Collection of related price values.
/// </summary>
public ICollection<PriceValue>? PriceValues { get; set; }

#endregion

/// <summary>
/// External value for external IT systems
/// </summary>
public string? ProgrammaticValue { get; set; }


public virtual CharacterGroup? CharacterGroup { get; set; }

public virtual int? CharacterGroupId { get; set; }
Expand Down
13 changes: 9 additions & 4 deletions src/JoinRpg.DataModel/Projects/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ public class Project : IProjectEntity

public virtual ICollection<Claim> Claims { get; set; }

public virtual ICollection<FinanceOperation> FinanceOperations { get; set; }

public virtual ProjectDetails Details { get; set; }

public virtual ICollection<PlotFolder> PlotFolders { get; set; }

Project IProjectEntity.Project => this;

#region Finance

public virtual ICollection<ProjectFeeSetting> ProjectFeeSettings { get; set; }

public virtual ICollection<PaymentType> PaymentTypes { get; set; }

public virtual ICollection<MoneyTransfer> MoneyTransfers { get; set; }

public virtual ICollection<FinanceOperation> FinanceOperations { get; set; }

#endregion

public DateTime CharacterTreeModifiedAt { get; set; }

#region helper properties
Expand All @@ -53,7 +60,5 @@ public class Project : IProjectEntity

public virtual ICollection<GameReport2DTemplate> GameReport2DTemplates { get; set; }

public virtual ICollection<MoneyTransfer> MoneyTransfers { get; set; }

public virtual HashSet<KogdaIgraGame> KogdaIgraGames { get; set; } = null!;
}
52 changes: 52 additions & 0 deletions src/JoinRpg.DataModel/Projects/ProjectDetails.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using JoinRpg.DataModel.Finances;
using JoinRpg.PrimitiveTypes.ProjectMetadata;

namespace JoinRpg.DataModel;
Expand All @@ -13,10 +14,61 @@ public class ProjectDetails : IValidatableObject
public bool EnableManyCharacters { get; set; }
public bool PublishPlot { get; set; }

#region Finance

public bool FinanceWarnOnOverPayment { get; set; } = true;

/// <summary>
/// The minimal required payment to be made to stop increasing the total price depending on date.
/// </summary>
/// <remarks>
/// This property is not mutually exclusive with <see cref="MinPaidPercentToFixPrice"/>.
/// When both defined, price will be fixed when the first condition has met.
/// </remarks>
[Range(0, int.MaxValue)]
public int? MinPaymentToFixPrice { get; set; }

/// <summary>
/// The minimal required payment in percent from total to be made to stop increasing the total price depending on date.
/// </summary>
/// <remarks>
/// This property is not mutually exclusive with <see cref="MinPaymentToFixPrice"/>.
/// When both defined, price will be fixed when the first condition has met.
/// </remarks>
[Range(0, 100)]
public int? MinPaidPercentToFixPrice { get; set; }

/// <summary>
/// When true, a discount could be set for a claim payment.
/// </summary>
public bool EnableDiscounts { get; set; }

/// <summary>
/// Describes the conditions a user should meet to get a discount.
/// </summary>
public MarkdownString DiscountConditions { get; set; } = new();

/// <summary>
/// Defines how the primary position in receipt should be identified.
/// </summary>
/// <remarks>The <see cref="ReceiptItemType.IncludeIntoPrimary"/> is not allowed here.</remarks>
public ReceiptItemType PrimaryPaymentReceiptItemType { get; set; } = ReceiptItemType.Service;

/// <summary>
/// The named used to display in receipt.
/// When not specified, it will be automatically combined from the project's name and the word "Билет".
/// </summary>
[StringLength(64)]
public string? PrimaryPaymentReceiptName { get; set; }

[Obsolete("Use the Discountable instead")]
public bool PreferentialFeeEnabled { get; set; } = false;

[Obsolete("Use the DiscountConditions instead")]
public MarkdownString PreferentialFeeConditions { get; set; } = new MarkdownString();

#endregion

public bool EnableCheckInModule { get; set; } = false;
public bool CheckInProgress { get; set; } = false;
public bool AllowSecondRoles { get; set; } = false;
Expand Down
Loading