Commit c79cfa78 by 陶然

init

parents
################################################################################
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
################################################################################
/Src/obj/Debug
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances
{
internal class Configs
{
public const string TableNameAccount = "FINA_Accounts";
public const string TableNameAccountDetail = "FINA_AccountDetails";
public const string TableNameInvoice = "FINA_Invoices";
public const string TableNameInvoiceDetail = "FINA_InvoiceDetails";
public const string TableNameInvoiceTitle = "FINA_InvoicesTitles";
public const string TableNameApply = "FINA_Applies";
public const string TableNameApplyDetail = "FINA_ApplyDetails";
public const string TableNamePayment = "FINA_Payments";
public const string TableNameTransfer = "FINA_Transfers";
public const string TableNameSettlement = "FINA_Settlements";
public const string TableNameSettlementGroup = "FINA_SettlementGroups";
public const string TableNamePrefixSettlement = "Stms";
public const string TableNamePrefixSettlementDetail = "Stmd";
public const string TableNameBill = "FINA_Bills";
public const string TableNameBillDetail = "FINA_BillDetails";
public const string TableNameLoan = "FINA_Loans";
public const string TableNameLoanDetail = "FINA_LoanDetails";
public const string TableNamePay = "FINA_Pays";
public const string TableNameDiscount = "FINA_Discounts";
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "账户")]
[Alias(Configs.TableNameAccount)]
public class Account : EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasCreator
{
#region 账户所有人,禁止更新
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid OwnerKvid { get; set; }
[ApiMember(Description = "所有者名称")]
[IgnoreUpdate]
[StringLength(500), Required]
public string OwnerName { get; set; }
#endregion
#region 帐户相关 账户类型禁止更新
[ApiMember(Description = "账户名称,如果是Bank类型,可以写银行名称,如果是其它类型,这个应该是账户的友好名称,不应该和OwnerName重复")]
[StringLength(100)]
[Required]
public string Name { get; set; }
[ApiMember(Description = "账户编号")]
[StringLength(100),Default("")]
public string SerialNumber { get; set; }
[ApiMember(Description = "类型,账户类型")]
[StringLength(50), Required]
public AccountType Type { get; set; }
[ApiMember(Description = "失效日期,仅针对StoredCard储值卡 和 Coupon 优惠券")]
public DateTime? ExpiredDate { get; set; }
#endregion
#region 金额相关,账户单位禁止更新,额度和金额都是内部更新
[ApiMember(Description = "授信额度,仅针对存款账户")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal CreditLine { get; set; }
[ApiMember(Description = "当前金额")]
[InternalSetter]
[DecimalLength(15, 2), Required]
public decimal Amount { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Creator
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "组织机构名称,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[StringLength(200),CurrentOrganizationName]
public string OrganizationName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
#endregion
}
/// <summary>
/// 账户类型
/// </summary>
public enum AccountType
{
/// <summary>
/// 不支持的账户类型
/// </summary>
Unsupported = 0,
/// <summary>
///银行账户,可清零或调帐,不用于客户,只用于组织内
/// </summary>
Bank,
/// <summary>
///现金账户,用于收到的现金,只用于组织内,个人业务员使用,不用于客户,如业务员收取现金
/// </summary>
Cash,
/// <summary>
///刷卡账户,用于收到的刷卡,只用于组织内,个人业务员使用
/// </summary>
Pos,
AliPay,
WeChat,
/// <summary>
///存款账户,指客户的存款账户,同部门只能拥有一个, 多部门可存在多个账户
/// </summary>
Deposit,
/// <summary>
///优惠券,一次性使用,多不退少补
/// </summary>
Coupon,
/// <summary>
///储值卡,只能充值和消费,不能转出
/// </summary>
StoredCard,
/// <summary>
///业务专用账户,可设多个业务账户,用于记录不同业务的收入
/// </summary>
Biz,
/// <summary>
/// 平衡系统所有账户金额为0,一个系统仅有一个,即系统与系统外部有金额往来就新增账户明细
/// </summary>
Balance,
/// <summary>
/// 折扣账户,各部门仅有一个,也可所属到个人
/// </summary>
Discount,
/// <summary>
/// 退款账户,所属于系统使用退款账户
/// </summary>
Refund
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
//帐户明细,只记录往来变化和一些信息,其数据来源应该是对应于Payment
[Api(Description = "账户明细")]
[Alias(Configs.TableNameAccountDetail)]
public class AccountDetail:EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
#region 关联信息 Account,Offset Biz
[InternalSetter]
[DefaultEmptyGuid, Required]
public Guid AccountKvid { get; set; }
[ApiMember(Description = "业务Kvid")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200), Default("")]
public string BizId { get; set; }
[ApiMember(Description = "业务类型,指实体类型全名称")]
[InternalSetter]
[StringLength(200), Default("")]
public string BizType { get; set; }
#endregion
#region 收付双方 Payer付款单位,贷方 Payee收款单位,借方 禁止更新
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位,贷方")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerAccountKvid { get; set; }
[ApiMember(Description = "付款账户名称,如果是Bank类的,指银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerAccountName { get; set; }
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerAccountSerialNumber { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位,借方")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeAccountKvid { get; set; }
[ApiMember(Description = "收款账户名称")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeAccountName { get; set; }
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeAccountSerialNumber { get; set; }
#endregion
#region 金额相关
/// <summary>
/// 发生金额
/// </summary>
[ApiMember(Description = "发生金额,正数为入(借),负数为出(贷)正常全为正数,如果为负值,应该对调借贷双方")]
[IgnoreUpdate]
[DecimalLength(15, 2), Required]
public decimal AmountPayment { get; set; }
/// <summary>
/// 帐户余额
/// </summary>
[ApiMember(Description = "帐户余额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "分析")]
public class Analysis
{
public decimal Amount { get; set; }
public decimal AmountSettlement { get; set; }
public decimal AmountInvoice { get; set; }
public decimal AmountPayment { get; set; }
public decimal AmountUnpaid { get; set; }
public decimal AmountOffset { get; set; }
public int Quantity { get; set; }
public int QuantityInvoice { get; set; }
public int QuantitySettlement { get; set; }
public int QuantityPayment { get; set; }
public int QuantityUnpaid { get; set; }
public int QuantityOffset { get; set; }
public AnalysisType Type { get; set; }
public DateTime BeginTime { get; set; }
public DateTime EndTime { get; set; }
public string Summary { get; set; }
}
/// <summary>
/// 分析类型,默认按月,支持: 年,月,日,季度
/// </summary>
public enum AnalysisType
{
Unsupported = 0,
Month,
Day,
Season,
Year
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "账单")]
[Alias(Configs.TableNameBill)]
public class Bill : EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasCreator, IEntityHasUpdater
{
#region 关联信息 Owner
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[StringLength(500)]
public string OwnerName { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 账单双方 Payer需方单位 Payee供方单位
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
#endregion
#region 账单双方联系方式
[DefaultEmptyGuid]
public Guid PayerContactKvid { get; set; }
[ApiMember(Description = "付款联系人")]
[StringLength(100), Default("")]
public string PayerContactName { get; set; }
[ApiMember(Description = "付款联系电话")]
[StringLength(50), Default("")]
public string PayerContactNumber { get; set; }
[ApiMember(Description = "付款联系邮箱")]
[StringLength(100), Default("")]
public string PayerContactEmail { get; set; }
[ApiMember(Description = "付款联系地址")]
[StringLength(500), Default("")]
public string PayerContactAddress { get; set; }
[DefaultEmptyGuid]
public Guid PayeeContactKvid { get; set; }
[ApiMember(Description = "收款联系人")]
[StringLength(100), Default("")]
public string PayeeContactName { get; set; }
[ApiMember(Description = "收款联系电话")]
[StringLength(50), Default("")]
public string PayeeContactNumber { get; set; }
[ApiMember(Description = "收款联系邮箱")]
[StringLength(100), Default("")]
public string PayeeContactEmail { get; set; }
[ApiMember(Description = "收款联系地址")]
[StringLength(500), Default("")]
public string PayeeContactAddress { get; set; }
#endregion
#region 基本信息
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string SerialNumber { get; set; }
#endregion
#region 金额相关
[ApiMember(Description = "预结算金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPlan { get; set; }
[ApiMember(Description = "结算金额,实际结算金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "已付金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPayment { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "组织机构名称,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[StringLength(200), CurrentOrganizationName]
public string OrganizationName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
public class BillWithSettlements:Bill
{
[Ignore]
public List<Settlement> Settlements { get; set; }
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "账单明细")]
[Alias(Configs.TableNameBillDetail)]
public class BillDetail:EntityWithMetadata,IEntityInAssemblyDb,
IEntityHasSummary,IEntityHasRemark,
IEntityHasCreator, IEntityHasUpdater
{
#region 关联信息,禁止更新
[ApiMember(Description = "账单Kvid")]
[IgnoreUpdate]
[Required]
public Guid BillKvid { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[ApiMember(Description = "业务Kvid")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 账单明细相关
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string SerialNumber { get; set; }
[ApiMember(Description = "明细类型")]
[IgnoreUpdate]
[StringLength(50), Default("")]
public string Type { get; set; }
[ApiMember(Description = "结算金额,实际结算金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "折扣,实收款项少于应收款项时用,例如应收1100,实际客户支付了1000,剩余100 需要使用此对象记录下来")]
[Alias(Configs.TableNameDiscount)]
public class Discount:EntityWithMetadata,IEntityInAssemblyDb,
IEntityHasSummary,IEntityHasRemark,
IEntityHasCreator
{
#region 关联信息
/// <summary>
/// 所属部门
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid PayKvid { get; set; }
#endregion
#region 基本信息
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default(""), SerialNumber]
public string SerialNumber { get; set; }
/// <summary>
/// 记录从哪个账户上折扣的
/// </summary>
[ApiMember(Description = "账户Kvid,记录从哪个账户上折扣的")]
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid AccountKvid { get; set; }
[ApiMember(Description = "账户名称,记录从哪个账户上折扣的")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string AccountName { get; set; }
[ApiMember(Description = "账户流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string AccountSerialNumber { get; set; }
#endregion
#region 费用相关
[ApiMember(Description = "发生金额,抵扣的金额数")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经办人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
#endregion
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
//发票是财务人员操作,开具发票,可被相应的合同或其它认领,可拆分
//认领时需要提供甲乙双方名称,可由具体业务拆分发票
//状态 1:开具的发票 51:领取了的发票 52:寄出的发票 100:作废的发票
[Api(Description = "发票")]
[Alias(Configs.TableNameInvoice)]
public class Invoice : EntityWithMetadata,IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
#region 关联信息 Owner,Offset Biz,全内部设置
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
[ApiMember(Description = "收付款关联,针对Payment和Invoice多对多的关系 使用中间表Apply(开票申请)关联起来")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid ApplyKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid RootKvid { get; set; }
#endregion
#region 收付双方 Payer付款单位,贷方 Payee收款单位,借方 全禁止更新
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位,贷方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[ApiMember(Description ="付方税号,收票方税号")]
[IgnoreUpdate]
[StringLength(100)]
public string PayerTaxNumber { get; set; }
[ApiMember(Description = "公司地址")]
[StringLength(200), Default("")]
public string PayerCompanyAddress { get; set; }
[ApiMember(Description = "电话号码")]
[StringLength(50), Default("")]
public string PayerPhone { get; set; }
[ApiMember(Description = "付款账户名称,指开户银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerRegisteredBank { get; set; }
[ApiMember(Description = "付款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerBankAccount { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位,借方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
[ApiMember(Description = "收方税号,开票方税号")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeTaxNumber { get; set; }
[ApiMember(Description = "公司地址")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeCompanyAddress { get; set; }
[ApiMember(Description = "电话号码")]
[IgnoreUpdate]
[StringLength(50), Default("")]
public string PayeePhone { get; set; }
[ApiMember(Description = "收款账户名称,指开户银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeRegisteredBank { get; set; }
[ApiMember(Description = "收款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeBankAccount { get; set; }
#endregion
#region 发票相关
[ApiMember(Description = "发票代码")]
[InternalSetter]
[StringLength(100), Default("")]
public string SerialCode { get; set; }
[ApiMember(Description = "发票号码,唯一性,拆分的发票和原编号有关联性")]
[InternalSetter,Unique]
[StringLength(100), Default("")]
public string SerialNumber { get; set; }
[ApiMember(Description = "发票类型,普通,专用增值等")]
[IgnoreUpdate]
[StringLength(50), Required]
public string Type { get; set; }
[ApiMember(Description = "分类,借票,到账等")]
[StringLength(50), Default("")]
public string Category { get; set; }
#endregion
#region 金额和税
[ApiMember(Description = "发票税率")]
[IgnoreUpdate]
[DecimalLength(5, 4), Default(0)]
public decimal TaxRate { get; set; }
[ApiMember(Description = "收款金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPayment { get; set; }
[ApiMember(Description = "含税金额,合同或订单中使用的金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
[ApiMember(Description = "未税金额,Amount=AmountUntaxed+AmountTax")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountUntaxed { get; set; }
[ApiMember(Description = "税额,和计算结果Amount-Amount/(1+TaxRate)不能相差超过一个固定值")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountTax { get; set; }
#endregion
#region 拆分属性
//ParentKvid为 Empty为主发票,否则为拆分发票,不作统计用,只支持原发票拆分,拆分后不再支持再拆分
[InternalSetter]
[DefaultEmptyGuid]
public Guid ParentKvid { get; set; }
//如果被拆分后,这个金额应该是拆分项目Amount金额的统计
[ApiMember(Description = "关联金额,被业务关联掉的金额,正常应该和Amount相同")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountUsed { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime 全禁止更新
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[ApiMember(Description = "支付日期")]
[InternalSetter]
public DateTime? PayedTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description ="组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
/// <summary>
/// Status:1业务员借票申请,25申请通过工作流待财务审批,50财务通过申请,51待开票,52正在开票中 55,56开票失败 57驳回 maxvalue开票完成
/// 当多条借票申请对应收款时 生成一个总和的申请与收款关联 更新对应的发票的ApplyKvid为总和的Apply的Kvid 原有的申请Apply的ParentKvid记录总和Apply的Kvid
/// </summary>
[Api(Description = "发票申请")]
[Alias(Configs.TableNameApply)]
public class InvoiceApply : EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
#region 关联信息 Owner,Offset Biz,全内部设置
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid RootKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid ParentKvid { get; set; }
#endregion
#region 申请信息
[ApiMember(Description = "申请编号,唯一性")]
[InternalSetter, Unique]
[StringLength(100), Default(""),SerialNumber]
public string SerialNumber { get; set; }
[ApiMember(Description ="申请类型")]
[IgnoreUpdate]
[StringLength(50),Required]
public InvoiceApplyType OperateType { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位,贷方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[ApiMember(Description = "付方税号,收票方税号")]
[IgnoreUpdate]
[StringLength(100)]
public string PayerTaxNumber { get; set; }
[ApiMember(Description = "公司地址")]
[StringLength(200), Default("")]
public string PayerCompanyAddress { get; set; }
[ApiMember(Description = "电话号码")]
[StringLength(50), Default("")]
public string PayerPhone { get; set; }
[ApiMember(Description = "付款账户名称,指开户银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerRegisteredBank { get; set; }
[ApiMember(Description = "付款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerBankAccount { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位,借方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
[ApiMember(Description = "收方税号,开票方税号")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeTaxNumber { get; set; }
[ApiMember(Description = "公司地址")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeCompanyAddress { get; set; }
[ApiMember(Description = "电话号码")]
[IgnoreUpdate]
[StringLength(50), Default("")]
public string PayeePhone { get; set; }
[ApiMember(Description = "收款账户名称,指开户银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeRegisteredBank { get; set; }
[ApiMember(Description = "收款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeBankAccount { get; set; }
#endregion
#region 申请金额
[ApiMember(Description = "发票类型,普通,专用增值等")]
[InternalSetter]
[StringLength(50)]
public string Type { get; set; }
[ApiMember(Description = "申请金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "已开票金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountInvoice { get; set; }
[ApiMember(Description = "已关联金额,关联的收款或者刷卡现金账户金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountUsed { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime 全禁止更新
[ApiMember(Description = "申请日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "申请人")]
[IgnoreUpdate]
[StringLength(100)]
public string OperatorName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
}
public enum InvoiceApplyType
{
Unsupported = 0,
/// <summary>
/// 收款
/// </summary>
Payment,
/// <summary>
/// 业务
/// </summary>
Biz,
/// <summary>
/// 借票
/// </summary>
Debit,
/// <summary>
/// 关联,与收款一一对应用
/// </summary>
Related,
/// <summary>
/// 现金,财务用
/// </summary>
Cash,
/// <summary>
/// Pos机,财务用
/// </summary>
Pos,
AliPay,
WeChat
}
public enum InvoiceApplyStatus
{
/// <summary>
/// 默认状态
/// </summary>
Default = 0,
/// <summary>
/// 借票申请初始状态
/// </summary>
Debit = 1,
/// <summary>
/// 流程通过
/// </summary>
ProcessAdoption = 25,
/// <summary>
/// 财务审批通过
/// </summary>
FinancialApproval = 50,
/// <summary>
/// 财务执行开票动作
/// </summary>
FinancialExecute = 51,
/// <summary>
/// 进入自动开票流程
/// </summary>
AutoExecuting = 52,
/// <summary>
/// 自动开票生产XML文件失败
/// </summary>
ErrorXml = 55,
/// <summary>
/// 插入发票到数据库失败
/// </summary>
ErrorInsertInvoice = 56,
/// <summary>
/// 申请驳回
/// </summary>
ApplyReject = 57,
/// <summary>
/// 发票作废
/// </summary>
InvoiceOffset = 58,
/// <summary>
/// 申请合并
/// </summary>
ApplyMerge = 59,
/// <summary>
/// 红字冲销
/// </summary>
InvoiceNegative = 60,
/// <summary>
/// 申请完成,已开具发票
/// </summary>
Completed = int.MaxValue
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "申请明细")]
[Alias(Configs.TableNameApplyDetail)]
public class InvoiceApplyDetail:EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasRemark,
IEntityHasCreator, IEntityHasUpdater
{
#region 关联信息,禁止更新
[ApiMember(Description = "申请Kvid")]
[IgnoreUpdate]
[Required]
public Guid ApplyKvid { get; set; }
#endregion
#region 申请明细
[ApiMember(Description = "货品编号")]
//[IgnoreUpdate]
[StringLength(100), Default("")]
public string GoodsId { get; set; }
[ApiMember(Description = "条目名称,货物或应税劳务、服务名称")]
//[IgnoreUpdate,]
[StringLength(200), Required]
public string GoodsFullName { get; set; }
[ApiMember(Description = "货品规格")]
//[IgnoreUpdate,]
[StringLength(200), Default("")]
public string GoodsSpecifications { get; set; }
[ApiMember(Description = "货品型号")]
//[IgnoreUpdate,]
[StringLength(200), Default("")]
public string GoodsModel { get; set; }
[ApiMember(Description = "货品单位")]
//[IgnoreUpdate,]
[StringLength(50), Default("")]
public string GoodsUnit { get; set; }
[ApiMember(Description = "数量单位")]
[StringLength(50), Default("")]
public string QuantityUnit { get; set; }
[ApiMember(Description = "数量")]
[DecimalLength(10, 2), Default(0)]
public decimal Quantity { get; set; }
[ApiMember(Description = "单价(含税)")]
[DecimalLength(16, 8), Default(0)]
public decimal QuantityUnitPrice { get; set; }
[ApiMember(Description = "单价(不含税)")]
[DecimalLength(15, 2), Default(0)]
public decimal QuantityUnitPriceUntaxed { get; set; }
[ApiMember(Description = "税率")]
[IgnoreUpdate, InternalSetter]
[DecimalLength(5, 2), Required]
public decimal TaxRate { get; set; }
[ApiMember(Description = "含税金额,合同或订单中使用的金额")]
[DecimalLength(15, 2), Required]
public decimal Amount { get; set; }
[ApiMember(Description = "未税金额,Amount=AmountUntaxed+AmountTax")]
[DecimalLength(15, 2), Default(0)]
public decimal AmountUntaxed { get; set; }
[ApiMember(Description = "税额,和计算结果Amount*TaxRate不能相差超过一个固定值")]
[DecimalLength(15, 2), Default(0)]
public decimal AmountTax { get; set; }
#endregion
#region 备注
[ApiMember(Description = "备注")]
[StringLength(1000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作人Creator Updater
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "发票明细")]
[Alias(Configs.TableNameInvoiceDetail)]
public class InvoiceDetail : EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasRemark,
IEntityHasCreator, IEntityHasUpdater
{
#region 关联信息,禁止更新
[ApiMember(Description = "发票Kvid")]
[IgnoreUpdate]
[Required]
public Guid InvoiceKvid { get; set; }
#endregion
#region 发票明细
[ApiMember(Description = "货品编号")]
//[IgnoreUpdate]
[StringLength(100), Default("")]
public string GoodsId { get; set; }
[ApiMember(Description = "条目名称,货物或应税劳务、服务名称")]
//[IgnoreUpdate,]
[StringLength(200), Required]
public string GoodsFullName { get; set; }
[ApiMember(Description = "货品规格")]
//[IgnoreUpdate,]
[StringLength(200), Default("")]
public string GoodsSpecifications { get; set; }
[ApiMember(Description = "货品型号")]
//[IgnoreUpdate,]
[StringLength(200), Default("")]
public string GoodsModel { get; set; }
[ApiMember(Description = "货品单位")]
//[IgnoreUpdate,]
[StringLength(50), Default("")]
public string GoodsUnit { get; set; }
[ApiMember(Description = "数量单位")]
[StringLength(50), Default("")]
public string QuantityUnit { get; set; }
[ApiMember(Description = "数量")]
[DecimalLength(10, 2), Default(0)]
public decimal Quantity { get; set; }
[ApiMember(Description = "单价(含税)")]
[DecimalLength(16, 8), Default(0)]
public decimal QuantityUnitPrice { get; set; }
[ApiMember(Description = "单价(不含税)")]
[DecimalLength(15, 2), Default(0)]
public decimal QuantityUnitPriceUntaxed { get; set; }
[ApiMember(Description = "税率")]
[IgnoreUpdate,InternalSetter]
[DecimalLength(5, 2), Required]
public decimal TaxRate { get; set; }
[ApiMember(Description = "含税金额,合同或订单中使用的金额")]
[DecimalLength(15, 2), Required]
public decimal Amount { get; set; }
[ApiMember(Description = "未税金额,Amount=AmountUntaxed+AmountTax")]
[DecimalLength(15, 2),Default(0)]
public decimal AmountUntaxed { get; set; }
[ApiMember(Description = "税额,和计算结果AmountUntaxed*TaxRate不能相差超过一个固定值")]
[DecimalLength(15, 2), Default(0)]
public decimal AmountTax { get; set; }
#endregion
#region 备注
[ApiMember(Description = "备注")]
[StringLength(1000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作人Creator Updater
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
}
using Kivii.DataAnnotations;
namespace Kivii.Finances.Entities
{
[Api(Description = "发票抬头")]
[Alias(Configs.TableNameInvoiceTitle)]
public class InvoiceTitle:Entity, IEntityInAssemblyDb
{
[ApiMember(Description = "公司名称")]
[StringLength(50), Required,Unique]
public string CompanyName { get; set; }
[ApiMember(Description = "税号")]
[StringLength(30), Default("")]
public string TaxNumber { get; set; }
[ApiMember(Description = "公司地址")]
[StringLength(200),Default("")]
public string CompanyAddress { get; set; }
[ApiMember(Description = "电话号码")]
[StringLength(50),Default("")]
public string Phone { get; set; }
[ApiMember(Description = "开户银行")]
[StringLength(50)]
public string BankName { get; set; }
[ApiMember(Description = "银行账户")]
[StringLength(50), Default("")]
public string BankAccount { get; set; }
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "付款单")]
[Alias(Configs.TableNamePay)]
public class Pay : EntityWithMetadata, IEntityInAssemblyDb, IEntityHasBiz,
IEntityHasOffset, IEntityHasSummary,
IEntityHasRemark, IEntityHasCreator, IEntityHasUpdater
{
#region 关联信息 Owner,Biz
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 收付双方
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付方单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收方单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
#endregion
#region 基本信息
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string SerialNumber { get; set; }
[ApiMember(Description = "消费类型")]
[StringLength(50), Default("")]
public string Type { get; set; }
//[ApiMember(Description = "付款Kvid")]
//[InternalSetter]
//[DefaultEmptyGuid]
//public Guid PaymentKvid { get; set; }
[ApiMember(Description = "支付日期")]
[InternalSetter]
public DateTime? PayedTime { get; set; }
#endregion
#region 费用相关
/// <summary>
/// 结算金额,结算明细统计
/// </summary>
[ApiMember(Description = "结算金额,结算明细统计")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountSettlement { get; set; }
[ApiMember(Description = "实付金额,客户实际支付的金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "折扣金额,客户实际支付的金额少于结算金额时补齐的金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountDiscount { get; set; }
[ApiMember(Description = "已付金额,实付金额和折扣金额的和")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPayment { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
//[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经办人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "组织机构名称,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[StringLength(200), CurrentOrganizationName]
public string OrganizationName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
public enum PayType
{
Unsupported = 0,
/// <summary>
/// 付款记录支付
/// </summary>
Payment,
/// <summary>
/// 客户账户支付
/// </summary>
Account,
/// <summary>
/// 抵扣的
/// </summary>
Discount
}
public class PayingMethod
{
public PayType Type { get; set; }
public Guid Kvid { get; set; }
public decimal Amount { get; set; }
public string Remark { get; set; }
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "付款")]
[Alias(Configs.TableNamePayment)]
public class Payment : EntityWithMetadata,IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
#region 关联信息 Owner,Offset Biz
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[ApiMember(Description = "所属Kvid,一般用于这笔付款属于哪个部门")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
/// <summary>
/// 根kvid,原始的付款信息,所以拆分的N级子项都有共同的RootKvid
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid RootKvid { get; set; }
//ParentKvid为 Empty为主发票,否则为拆分收款,不作统计用
[InternalSetter]
[DefaultEmptyGuid]
public Guid ParentKvid { get; set; }
#endregion
#region 收付双方 Payer付款单位,贷方 Payee收款单位,借方,账号相关
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位,贷方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerAccountKvid { get; set; }
[ApiMember(Description = "付款账户")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerAccountOwnerName { get; set; }
[ApiMember(Description = "付款账户名称,如果是Bank类的,指银行")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerAccountName { get; set; }
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerAccountSerialNumber { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位,借方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeAccountKvid { get; set; }
[ApiMember(Description = "收款账户")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeAccountOwnerName { get; set; }
[ApiMember(Description = "收款账户名称")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeAccountName { get; set; }
[ApiMember(Description = "收款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeAccountSerialNumber { get; set; }
#endregion
#region 收付款相关
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default(""), SerialNumber]
public string SerialNumber { get; set; }
[ApiMember(Description = "付款类型,现金、银行汇款等")]
[IgnoreUpdate]
[StringLength(100), Required]
public PaymentType Type { get; set; }
[ApiMember(Description = "发生金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "已用金额,被业务关联掉的金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountUsed { get; set; }
//如果被拆分后,这个金额应该是拆分项目被关联金额的统计
[ApiMember(Description = "拆分金额,被拆分掉的金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountSplited { get; set; }
[ApiMember(Description = "开票金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountInvoice { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经办人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid AuditorKvid { get; set; }
[ApiMember(Description ="审核人")]
[IgnoreUpdate]
[StringLength(100),Default("")]
public string AuditorName { get; set; }
[ApiMember(Description ="组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
public enum PaymentType
{
Unsupported = 0,
/// <summary>
/// 银行流水
/// </summary>
Bank,
/// <summary>
/// 内部拆分,Biz业务认领使用类型
/// </summary>
Split,
/// <summary>
/// 非业务使用
/// </summary>
UnBiz,
/// <summary>
/// 退款
/// </summary>
Refund,
/// <summary>
/// 现金,直接从现金内部账户拨款到Payment的类型(区别于Account类型[消费使用],开票申请使用)
/// </summary>
Cash,
/// <summary>
/// 支付宝支付,暂无使用
/// </summary>
AliPay,
/// <summary>
/// 微信支付,暂无使用
/// </summary>
WeChat,
/// <summary>
/// 动态拆分,必有Biz,即为用于关联支付Pay业务,子集Pay用于结算,当有Pay冲销则此状态Amount AmountUsed AmountSplited都发生变化
/// </summary>
DynamicSplit,
/// <summary>
/// 刷卡,直接从刷卡内部账户拨款到Payment的类型(区别于Account类型[消费使用],开票申请使用)
/// </summary>
Pos,
/// <summary>
/// 支付,此类型必有Biz,即为动态拆分的子集用于关联具体Settlement业务
/// </summary>
Pay,
/// <summary>
/// 转账,必有Biz
/// </summary>
Transfer
}
}
using Kivii.DataAnnotations;
using Kivii.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
public abstract class SettlementBase : EntityWithMetadata
{
#region 关联信息 Owner,Offset
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[StringLength(500)]
public string OwnerName { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
#endregion
#region 业务关联(结算财务表中,Owner记录所属部门,Biz记录关联业务)
[ApiMember(Description = "业务Kvid,根据结算精细度,Settlement中的BizKvid可能会和SettlementDetail中的BizKvid一致," +
"查询时需要同时查询两表数据")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 结算双方 Payer需方单位 Payee供方单位
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付方单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收方单位")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
#endregion
#region 结算相关
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public virtual string SerialNumber { get; set; }
//不使用枚举型,不确定的类型
[ApiMember(Description = "结算类型")]
[InternalSetter]
[StringLength(50), Default("")]
public string Type { get; set; }
[ApiMember(Description = "预结算金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPlan { get; set; }
[ApiMember(Description = "结算金额,实际结算金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "已付金额")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPayment { get; set; }
[ApiMember(Description = "支付Kvid")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid PayKvid { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "组织机构名称,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[StringLength(200), CurrentOrganizationName]
public string OrganizationName { get; set; }
[ApiMember(Description ="组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大3000字")]
[StringLength(3000), Default("")]
public string Remark { get; set; }
#endregion
/// <summary>
/// 用于创建时 接口提供批量结算时使用
/// </summary>
[Ignore]
public List<SettlementDetail> Details { get; set; }
}
/// <summary>
/// 财务模块中的结算
/// </summary>
[Api(Description = "结算")]
[Alias(Configs.TableNameSettlement)]
public class Settlement : SettlementBase, IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public override string SerialNumber { get; set; }
//[ApiMember(Description = "已开票金额")]
//[InternalSetter]
//[DecimalLength(15, 2), Default(0)]
//public decimal AmountInvoice { get; set; }
private static MethodInfo _realMethod = null;
public object GetSettlementDetails()
{
var rtns = new List<SettlementDetail>();
Type entityType = Text.AssemblyUtils.FindType(this.BizType);
if (entityType == null) return rtns;
if (_realMethod == null)
{
var function = new Func<object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
return _realMethod.MakeGenericMethod(entityType).Invoke(this, null);
}
private object onExecution<T>() where T : IEntity
{
var rtns = new List<SettlementDetail>();
var conn = KiviiContext.GetOpenedDbConnection<EntitySettlementDetail<T>>();
var settlementDetals = conn.Select<EntitySettlementDetail<T>>(o => o.SettlementKvid == this.Kvid);
if (settlementDetals.IsNullOrEmpty()) return rtns;
return settlementDetals;
}
}
[Alias(Configs.TableNamePrefixSettlement)]
public class EntitySettlement<T> : SettlementBase, IEntityInGenericTypeDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public override string SerialNumber { get; set; }
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "结算分析")]
[Alias(Configs.TableNameSettlement)]
public class SettlementAnalysis:EntityWithMetadata, IEntityInAssemblyDb
{
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
public abstract class SettlementDetailBase : EntityWithMetadata
{
#region 关联信息,禁止更新
[ApiMember(Description = "结算Kvid")]
[IgnoreUpdate]
[Required]
public Guid SettlementKvid { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[ApiMember(Description = "业务Kvid,根据结算精细度,Settlement中的BizKvid可能会和SettlementDetail中的BizKvid一致," +
"查询时需要同时查询两表数据")]
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 结算明细
[ApiMember(Description = "货品编号")]
[StringLength(100), Default("")]
public string GoodsId { get; set; }
[ApiMember(Description = "条目名称,货物、服务等名称")]
[StringLength(200), Default("")]
public string GoodsFullName { get; set; }
[ApiMember(Description = "条目Kvid")]
[DefaultEmptyGuid]
public Guid GoodsKvid { get; set; }
[ApiMember(Description = "规格")]
[StringLength(200), Default("")]
public string GoodsSpecifications { get; set; }
[ApiMember(Description = "型号")]
[StringLength(200), Default("")]
public string GoodsModel { get; set; }
[ApiMember(Description = "货品单位")]
[StringLength(50)]
public string GoodsUnit { get; set; }
[ApiMember(Description = "结算数量,即实际结算数量")]
[DecimalLength(10, 2), Default(0)]
public decimal Quantity { get; set; }
[ApiMember(Description = "数量单位")]
[StringLength(50), Default("")]
public string QuantityUnit { get; set; }
[ApiMember(Description = "单价")]
[DecimalLength(10, 2), Default(0)]
public decimal QuantityUnitPrice { get; set; }
[ApiMember(Description = "预结算数量")]
[DecimalLength(10, 2), Default(0)]
public decimal QuantityPlan { get; set; }
//[ApiMember(Description = "预数量单位")]
//[StringLength(50), Default("")]
//public string QuantityUnitPlan { get; set; }
[ApiMember(Description = "预单价")]
[DecimalLength(10, 2), Default(0)]
public decimal QuantityUnitPricePlan { get; set; }
[ApiMember(Description = "预结算金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal AmountPlan { get; set; }
[ApiMember(Description = "结算金额,实际结算金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "明细类型")]
[IgnoreUpdate]
[StringLength(50), Default("")]
public string Type { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
#region 备注
[ApiMember(Description = "备注")]
[StringLength(3000), Default("")]
public string Remark { get; set; }
#endregion
#region 操作相关 Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
}
[Api(Description = "结算明细,仅数据结构,不存数据库")]
//[Alias(Configs.TableNameSettlementDetail)]
public class SettlementDetail : SettlementDetailBase,
IEntityHasRemark, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater
{
}
[Alias(Configs.TableNamePrefixSettlementDetail)]
public class EntitySettlementDetail<T> : SettlementDetailBase, IEntityInGenericTypeDb,
IEntityHasRemark, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater
{
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "结算分组")]
[Alias(Configs.TableNameSettlementGroup)]
public class SettlementGroup : EntityWithMetadata, IEntityInAssemblyDb
{
/// <summary>
/// 一般用于所属部门使用
/// </summary>
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[StringLength(500)]
public string OwnerName { get; set; }
[ApiMember(Description = "付方单位")]
[IgnoreUpdate]
[StringLength(100), Required, Unique]
public string PayerName { get; set; }
[ApiMember(Description = "类型")]
[InternalSetter]
[StringLength(500), Required]
public string Type { get; set; }
[ApiMember(Description = "结算金额总和")]
[InternalSetter]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "结算数量总和")]
[InternalSetter]
[Default(0)]
public int Quantity { get; set; }
}
}
using Kivii.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Entities
{
[Api(Description = "内部付款,用于内部账户之间转账使用,区分开实际银行等方式打款来的Payment表")]
[Alias(Configs.TableNameTransfer)]
public class Transfer:EntityWithMetadata, IEntityInAssemblyDb,
IEntityHasSummary, IEntityHasRemark,
IEntityHasOffset, IEntityHasBiz,
IEntityHasCreator, IEntityHasUpdater, IEntityHasOrganization
{
#region 关联信息 Owner,Offset Biz
/// <summary>
/// 一般不使用此字段,预留
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OwnerKvid { get; set; }
[InternalSetter]
[StringLength(500)]
public string OwnerName { get; set; }
/// <summary>
/// 冲帐关联
/// </summary>
[InternalSetter]
[DefaultEmptyGuid]
public Guid OffsetKvid { get; set; }
[InternalSetter]
[DefaultEmptyGuid]
public Guid BizKvid { get; set; }
[ApiMember(Description = "业务流水号")]
[InternalSetter]
[StringLength(200)]
public string BizId { get; set; }
[ApiMember(Description = "业务类型")]
[InternalSetter]
[StringLength(100)]
public string BizType { get; set; }
#endregion
#region 收付双方 Payer付款单位,贷方 Payee收款单位,借方
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayerKvid { get; set; }
[ApiMember(Description = "付款单位,贷方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayerName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid, Required]
public Guid PayeeKvid { get; set; }
[ApiMember(Description = "收款单位,借方")]
[IgnoreUpdate]
[StringLength(100), Required]
public string PayeeName { get; set; }
#endregion
#region 收付款账号相关
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayerAccountKvid { get; set; }
[ApiMember(Description = "付款账户")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerAccountOwnerName { get; set; }
[ApiMember(Description = "付款账户名称")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayerAccountName { get; set; }
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayerAccountSerialNumber { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid PayeeAccountKvid { get; set; }
[ApiMember(Description = "收款账户")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeAccountOwnerName { get; set; }
[ApiMember(Description = "收款账户名称")]
[IgnoreUpdate]
[StringLength(200), Default("")]
public string PayeeAccountName { get; set; }
[ApiMember(Description = "收款账户卡号")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string PayeeAccountSerialNumber { get; set; }
#endregion
#region 操作相关 Organization Creator Updater Operator OperateTime
[ApiMember(Description = "操作日期")]
[IgnoreUpdate]
[Required]
public DateTime OperateTime { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid OperatorKvid { get; set; }
[ApiMember(Description = "经 办 人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string OperatorName { get; set; }
[IgnoreUpdate]
[DefaultEmptyGuid]
public Guid AuditorKvid { get; set; }
[ApiMember(Description = "审核人")]
[IgnoreUpdate]
[StringLength(100), Default("")]
public string AuditorName { get; set; }
[ApiMember(Description = "组织机构Id,当组织有子组织时区分所属组织")]
[IgnoreUpdate]
[CurrentOrganizationKvid]
public Guid OrganizationKvid { get; set; }
[ApiMember(Description = "创建人Kvid ")]
[IgnoreUpdate]
[CurrentMemberKvid]
public Guid CreatorKvid { get; set; }
[ApiMember(Description = "创建人")]
[IgnoreUpdate]
[StringLength(50), CurrentMemberName]
public string CreatorName { get; set; }
[ApiMember(Description = "更新人Kvid ")]
[CurrentMemberKvid]
public Guid UpdaterKvid { get; set; }
[ApiMember(Description = "更新人")]
[StringLength(50), CurrentMemberName]
public string UpdaterName { get; set; }
#endregion
#region 文本 摘要 备注
[ApiMember(Description = "摘要,最大500字")]
[IgnoreUpdate]
[StringLength(500), Default("")]
public string Summary { get; set; }
[ApiMember(Description = "备注,最大2000字")]
[StringLength(2000), Default("")]
public string Remark { get; set; }
#endregion
#region 收付款相关
[ApiMember(Description = "流水号")]
[IgnoreUpdate]
[StringLength(100), Default(""), SerialNumber]
public string SerialNumber { get; set; }
[ApiMember(Description = "付款类型,现金、银行汇款等")]
[IgnoreUpdate]
[StringLength(100), Required]
public TransferType Type { get; set; }
[ApiMember(Description = "发生金额")]
[IgnoreUpdate]
[DecimalLength(15, 2), Default(0)]
public decimal Amount { get; set; }
[ApiMember(Description = "单位")]
[IgnoreUpdate]
[StringLength(50), Required]
public CurrencyUnit Currency { get; set; }
#endregion
}
public enum TransferType
{
Unsupported = 0,
/// <summary>
/// 现金业务,充值和提现
/// </summary>
Deposit,
/// <summary>
/// 内部账号转账
/// </summary>
Transfer,
/// <summary>
/// 合同付款业务
/// </summary>
Contract,
/// <summary>
/// 贷款业务
/// </summary>
Loan,
/// <summary>
/// 其它类型的业务转账
/// </summary>
Biz
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace Kivii.Finances
{
public static class AccountExtension
{
private static Dictionary<string, Account> _balanceAccounts = new Dictionary<string, Account>();
private static Dictionary<string, Account> _bizAccounts = new Dictionary<string, Account>();
private static Dictionary<string, Account> _discountAccounts = new Dictionary<string, Account>();
private static Dictionary<string, Account> _personalAccounts = new Dictionary<string, Account>();
/// <summary>
/// 获取系统平衡帐户,一个币种只有一个
/// </summary>
/// <param name="payment"></param>
/// <returns></returns>
public static Account GetBalanceAccount(this CurrencyUnit currency)
{
(currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位,不能查询平衡账户");
var key = KiviiContext.GetUrnKey(currency.ToString());
//如果包含这个币种,直接返回
if (_balanceAccounts.ContainsKey(key))
{
return _balanceAccounts[key];
}
var conn = KiviiContext.GetOpenedDbConnection<Account>();
//得到所有平衡帐户,并赋值给静态变量,减少数据库操作
var balanceAccounts = conn.Select<Account>(o => o.Type == AccountType.Balance);
foreach (var account in balanceAccounts)
{
_balanceAccounts[KiviiContext.GetUrnKey(account.Currency.ToString())] = account;
}
//如果从数据库中得到了,直接返回
if (_balanceAccounts.ContainsKey(key))
{
return _balanceAccounts[key];
}
//每个币种只有一个平衡帐户,通过自定义的Kvid防止多系统或多线程冲突引起在数据库中创建多个平衡帐户,Kvid的开头为BALANCE的象形写法
var currencyString = ((int)currency).ToString().PadLeft(4, '0');
var balanceKvid = new Guid($"BA1A0CE0-{currencyString}-0000-0000-000000000000");
Account rtns = new Account();
rtns.Kvid = balanceKvid;
rtns.Type = AccountType.Balance;
rtns.Currency = currency;
rtns.OwnerKvid = Guid.Empty;
rtns.OwnerName = "K5平台";
rtns.Name = "系统平衡帐户";
rtns.SerialNumber = "";
rtns.CreditLine = 0;
rtns.Amount = 0;
rtns.Summary = "系统内部平衡帐户,禁止操作";
rtns.Remark = "";
if (conn.Insert(rtns) == 1)
{
_balanceAccounts[KiviiContext.GetUrnKey(rtns.Currency.ToString())] = rtns;
return rtns;
}
throw new Exception("系统创建平衡帐户失败,请联系系统管理员。");
}
/// <summary>
/// 获取折扣账户
/// </summary>
/// <param name="currency"></param>
/// <returns></returns>
public static Account GetDiscountAccount(this CurrencyUnit currency)
{
(currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位,不能查询折扣账户");
var key = KiviiContext.GetUrnKey($"{AccountType.Discount}{currency}{KiviiContext.CurrentMember.DepartmentKvid}");
//如果包含这个币种,直接返回
if (_discountAccounts.ContainsKey(key))
{
return _discountAccounts[key];
}
var conn = KiviiContext.GetOpenedDbConnection<Account>();
//得到所有平衡帐户,并赋值给静态变量,减少数据库操作
var bizAccounts = conn.Select<Account>(o => o.Type == AccountType.Discount);
foreach (var account in bizAccounts)
{
_discountAccounts[KiviiContext.GetUrnKey($"{AccountType.Discount}{account.Currency}{KiviiContext.CurrentMember.DepartmentKvid}")] = account;
}
//如果从数据库中得到了,直接返回
if (_discountAccounts.ContainsKey(key))
{
return _discountAccounts[key];
}
var bizKvid = Guid.NewGuid();
Account rtns = new Account();
rtns.Kvid = bizKvid;
rtns.Type = AccountType.Discount;
rtns.Currency = currency;
rtns.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
rtns.OwnerName = KiviiContext.CurrentMember.DepartmentName;
rtns.Name = $"折扣帐户({rtns.OwnerName})";
rtns.SerialNumber = "";
rtns.CreditLine = 0;
rtns.Amount = 0;
rtns.Summary = "";
rtns.Remark = "";
if (conn.Insert(rtns) == 1)
{
_discountAccounts[KiviiContext.GetUrnKey($"{AccountType.Discount}{rtns.Currency}{KiviiContext.CurrentMember.DepartmentKvid}")] = rtns;
return rtns;
}
throw new Exception("系统创建折扣帐户失败,请联系系统管理员。");
}
/// <summary>
/// 获取业务账户
/// </summary>
/// <param name="currency"></param>
/// <returns></returns>
public static Account GetBizAccount(this CurrencyUnit currency)
{
(currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位,不能查询业务账户");
var key = KiviiContext.GetUrnKey($"{AccountType.Biz}{currency}{KiviiContext.CurrentMember.DepartmentKvid}");
//如果包含这个币种,直接返回
if (_bizAccounts.ContainsKey(key))
{
return _bizAccounts[key];
}
var conn = KiviiContext.GetOpenedDbConnection<Account>();
//得到所有平衡帐户,并赋值给静态变量,减少数据库操作
var bizAccounts = conn.Select<Account>(o => o.Type == AccountType.Biz);
foreach (var account in bizAccounts)
{
_bizAccounts[KiviiContext.GetUrnKey($"{AccountType.Biz}{account.Currency}{KiviiContext.CurrentMember.DepartmentKvid}")] = account;
}
//如果从数据库中得到了,直接返回
if (_bizAccounts.ContainsKey(key))
{
return _bizAccounts[key];
}
var bizKvid = Guid.NewGuid();
Account rtns = new Account();
rtns.Kvid = bizKvid;
rtns.Type = AccountType.Biz;
rtns.Currency = currency;
rtns.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
rtns.OwnerName = KiviiContext.CurrentMember.DepartmentName;
rtns.Name = $"业务帐户({rtns.OwnerName})";
rtns.SerialNumber = "";
rtns.CreditLine = 0;
rtns.Amount = 0;
rtns.Summary = "";
rtns.Remark = "";
if (conn.Insert(rtns) == 1)
{
_bizAccounts[KiviiContext.GetUrnKey($"{AccountType.Biz}{rtns.Currency}{KiviiContext.CurrentMember.DepartmentKvid}")] = rtns;
return rtns;
}
throw new Exception("系统创建业务帐户失败,请联系系统管理员。");
}
/// <summary>
/// 获取个人帐户,例如现金账户
/// </summary>
/// <param name="payment"></param>
/// <returns></returns>
public static Account GetPersonalAccount(this CurrencyUnit currency, AccountType accountType)
{
(currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位,不能查询个人账户");
(accountType != AccountType.Cash && accountType != AccountType.Pos).ThrowIfTrue("不支持的账户类型!,个人账户仅支持现金和刷卡账户!");
var key = KiviiContext.GetUrnKey($"{accountType}{currency}{KiviiContext.CurrentMember.Kvid}");
//如果包含这个币种,直接返回
if (_personalAccounts.ContainsKey(key))
{
return _personalAccounts[key];
}
var conn = KiviiContext.GetOpenedDbConnection<Account>();
//得到所有个人帐户,并赋值给静态变量,减少数据库操作
var personalAccount = conn.Single<Account>(o => o.Type == accountType && o.OwnerKvid == KiviiContext.CurrentMember.Kvid);
if (personalAccount != null) _personalAccounts[KiviiContext.GetUrnKey($"{personalAccount.Type}{currency}{personalAccount.OwnerKvid}")] = personalAccount;
//如果从数据库中得到了,直接返回
if (_personalAccounts.ContainsKey(key))
{
return _personalAccounts[key];
}
Account rtns = new Account();
rtns.Kvid = Guid.NewGuid();
rtns.Type = accountType;
rtns.Currency = currency;
rtns.OwnerKvid = KiviiContext.CurrentMember.Kvid;
rtns.OwnerName = KiviiContext.CurrentMember.FullName;
rtns.Name = $"{rtns.OwnerName}{accountType}账户";
rtns.SerialNumber = $"{accountType}{(DateTime.Now.Ticks - DateTime.Parse("1970-01-01").Ticks) / 10000}";
rtns.CreditLine = 0;
rtns.Amount = 0;
rtns.Summary = "";
rtns.Remark = "";
if (conn.Insert(rtns) == 1)
{
_personalAccounts[KiviiContext.GetUrnKey($"{rtns.Type}{currency}{rtns.OwnerKvid}")] = rtns;
return rtns;
}
throw new Exception("系统创建个人帐户失败,请联系系统管理员。");
}
/// <summary>
/// 新增账户明细
/// </summary>
/// <param name="accountDetail"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static Account Insert(this AccountDetail accountDetail, IDbConnection conn = null)
{
accountDetail.ThrowIfNull("账户明细不能为空!");
(accountDetail.Amount == 0).ThrowIfFalse("账户余额不能被赋除0之外的值!");
(accountDetail.AmountPayment <= 0).ThrowIfTrue("账户明细发生金额不能少于0!");
accountDetail.AccountKvid.ThrowIfEmpty("账户Kvid不能为空!");
accountDetail.PayeeAccountKvid.ThrowIfEmpty("收款账户不能为空!");
accountDetail.PayerAccountKvid.ThrowIfEmpty("付款账户不能为空!");
bool useTransaction = conn == null;//是否启用事务,如果外部未传入conn,启用内部事务
if (conn == null) conn = KiviiContext.GetOpenedDbConnection<AccountDetail>();
var account = conn.SingleById<Account>(accountDetail.AccountKvid);
account.ThrowIfNull("未找到此账户!");
IDbTransaction trans = null;//事务,如果外部来了Connection,不生成事务
if (useTransaction) trans = conn.OpenTransaction();
try
{
decimal amount = 0;
if (accountDetail.PayerAccountKvid == account.Kvid) amount = 0 - accountDetail.AmountPayment;
if (accountDetail.PayeeAccountKvid == account.Kvid) amount = accountDetail.AmountPayment;
//updateAdd中Amount是累加的
(conn.UpdateAdd(() => new Account { Amount = amount }, o => o.Kvid == account.Kvid) == 1).ThrowIfFalse("更新帐户数据错误");
accountDetail.Amount = account.Amount + amount;
(conn.Insert(accountDetail) == 1).ThrowIfFalse("插入帐户明细错误!");
trans?.Commit();
return account;
}
catch (Exception ex)
{
trans?.Rollback();
throw ex;
}
}
}
}
using Kivii.Finances.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances
{
public static class BillExtension
{
public static BillDetail Offset(this BillDetail billDetail,string remark)
{
billDetail.ThrowIfNull("账单明细不能为空!");
(billDetail.OffsetKvid != Guid.Empty).ThrowIfTrue("当前账单明细已作废!无需重复操作!");
#region payment冲账
var newBillDetailKvid = Guid.NewGuid();
billDetail.OffsetKvid = newBillDetailKvid;
billDetail.AddOnlyProperties(o => o.OffsetKvid);
billDetail.Remark = $"{remark}";
billDetail.AddOnlyProperties(o => o.Remark);
//新建一个和原来一样的
var offsetBillDetail = new BillDetail();
offsetBillDetail.PopulateWith(billDetail);
offsetBillDetail.OffsetKvid = billDetail.Kvid;//新的冲帐关联用原来的Kvid
offsetBillDetail.Kvid = newBillDetailKvid;
offsetBillDetail.SerialNumber = billDetail.SerialNumber.IsNullOrEmpty() ? "" : ("-" + billDetail.SerialNumber);
offsetBillDetail.Remark = $"{remark}";
offsetBillDetail.Amount = 0 - offsetBillDetail.Amount;//反向值
#endregion
return offsetBillDetail;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances
{
public static class InvoiceApplyExtension
{
/// <summary>
/// 获取子发票的流水号
/// </summary>
/// <param name="invoice"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static string GetSubSerialNumber(this Invoice invoice, IDbConnection conn = null)
{
invoice.RootKvid.ThrowIfEmpty("RootKvid不能为空");
if (conn == null) conn = KiviiContext.GetOpenedDbConnection<Invoice>();
var rootInvoice = conn.SingleById<Invoice>(invoice.RootKvid);
rootInvoice.ThrowIfNull("主发票记录不存在");
var allcount = conn.Count<Invoice>(o => o.RootKvid == rootInvoice.Kvid);
//allcount不需要加1,因为Root的也统计进去了
return $"{rootInvoice.SerialNumber}-{allcount}";
}
/// <summary>
/// Payment与Invoice产生关联的方法
/// </summary>
/// <param name="invoices"></param>
/// <param name="payments"></param>
/// <returns></returns>
public static List<Invoice> Correlating(this List<Invoice> invoices, List<Payment> payments)
{
payments.ThrowIfNullOrEmpty("要关联的Payments不能为空!");
invoices.ThrowIfNullOrEmpty("要关联的Invoices不能为空!");
(payments.Exists(o => o.Type != PaymentType.Bank && o.Type != PaymentType.AliPay && o.Type != PaymentType.Cash && o.Type != PaymentType.Pos && o.Type != PaymentType.Split && o.Type != PaymentType.WeChat)).ThrowIfTrue("请选择正确收款进行关联");
var payedTime = payments.Max(o => o.OperateTime);
var rtns = new List<Invoice>();
foreach (var payment in payments)
{
//说明已经建立关联关系的payment 跳过
if (payment.AmountInvoice >= payment.Amount) continue;
foreach (var invoice in invoices)
{
var amountCorrelating = invoice.Amount - invoice.AmountPayment;//要关联的金额
if (amountCorrelating <= 0) continue;
var amountLeave = payment.Amount - payment.AmountInvoice;//可以开票的金额
if (amountLeave <= 0) continue;
var amount = amountLeave <= amountCorrelating ? amountLeave : amountCorrelating;
invoice.PayedTime = payedTime;
invoice.AmountPayment += amount;
#region 建立关联关系
var relationInvoice = new Invoice();
relationInvoice.PopulateInstance(invoice);
relationInvoice.SerialNumber = string.Empty;
relationInvoice.Kvid = Guid.NewGuid();
relationInvoice.ParentKvid = invoice.Kvid;
relationInvoice.BizId = payment.SerialNumber;
relationInvoice.BizKvid = payment.RootKvid;//记录根的Kvid
relationInvoice.BizType = typeof(Payment).FullName;
relationInvoice.Type = "Relation";
relationInvoice.PayedTime = payedTime;
relationInvoice.Amount = amount;
relationInvoice.AmountPayment = amount;
//不含税金额
relationInvoice.AmountUntaxed = Math.Round(relationInvoice.Amount / (1 + relationInvoice.TaxRate), 2);
//税额
relationInvoice.AmountTax = relationInvoice.Amount - relationInvoice.AmountUntaxed;
rtns.Add(relationInvoice);
#endregion
payment.AmountInvoice += amount;
}
payment.AddOnlyProperties(o => o.AmountInvoice);
}
return rtns;
}
/// <summary>
/// 发票明细拆分
/// </summary>
/// <param name="invoiceApplyDetail"></param>
/// <param name="limit">拆分临界值</param>
/// <returns></returns>
internal static List<InvoiceApplyDetail> SplitApplyDetail(this InvoiceApplyDetail invoiceApplyDetail, decimal limit)
{
List<InvoiceApplyDetail> applyDetails = new List<InvoiceApplyDetail>();
#region 金额超过10万需要分开
if (invoiceApplyDetail.Amount <= limit)
{
invoiceApplyDetail.GoodsId = invoiceApplyDetail.GoodsId.IsNullOrEmpty() ? "组1" : invoiceApplyDetail.GoodsId;
//不含税金额
invoiceApplyDetail.AmountUntaxed = Math.Round(invoiceApplyDetail.Amount / (1 + invoiceApplyDetail.TaxRate), 2);
//税额
invoiceApplyDetail.AmountTax = invoiceApplyDetail.Amount - invoiceApplyDetail.AmountUntaxed;
//不含税单价
invoiceApplyDetail.QuantityUnitPriceUntaxed = Math.Round(invoiceApplyDetail.AmountUntaxed / invoiceApplyDetail.Quantity, 4);
//含税单价
invoiceApplyDetail.QuantityUnitPrice = Math.Round(invoiceApplyDetail.Amount / invoiceApplyDetail.Quantity, 4);
applyDetails.Add(invoiceApplyDetail);
}
else
{
var count = (int)(invoiceApplyDetail.Amount / limit);
for (var i = 0; i <= count - 1; i++)
{
var detail = new InvoiceApplyDetail();
detail.GoodsId = $"发票{i}";
detail.GoodsFullName = invoiceApplyDetail.GoodsFullName;
detail.GoodsSpecifications = invoiceApplyDetail.GoodsSpecifications;
detail.QuantityUnit = invoiceApplyDetail.QuantityUnit;
detail.QuantityUnitPriceUntaxed = invoiceApplyDetail.QuantityUnitPriceUntaxed;
detail.Quantity = invoiceApplyDetail.Quantity;
detail.TaxRate = invoiceApplyDetail.TaxRate;
detail.Amount = limit;
//不含税金额
detail.AmountUntaxed = Math.Round(detail.Amount / (1 + detail.TaxRate), 2);
//税额
detail.AmountTax = detail.Amount - detail.AmountUntaxed;
//不含税单价
detail.QuantityUnitPriceUntaxed = Math.Round(detail.AmountUntaxed / detail.Quantity, 4);
//含税单价
detail.QuantityUnitPrice = Math.Round(detail.Amount / detail.Quantity, 4);
applyDetails.Add(detail);
}
var leftAmount = invoiceApplyDetail.Amount - limit * count;
if (leftAmount > 0)
{
var item = new InvoiceApplyDetail();
item.GoodsId = $"发票{count}";
item.GoodsFullName = invoiceApplyDetail.GoodsFullName;
item.GoodsSpecifications = invoiceApplyDetail.GoodsSpecifications;
item.QuantityUnit = invoiceApplyDetail.QuantityUnit;
item.QuantityUnitPriceUntaxed = invoiceApplyDetail.QuantityUnitPriceUntaxed;
item.Quantity = invoiceApplyDetail.Quantity;
item.TaxRate = invoiceApplyDetail.TaxRate;
item.Amount = invoiceApplyDetail.Amount - limit * count;
item.AmountUntaxed = Math.Round(item.Amount / (1 + item.TaxRate), 2);
item.AmountTax = item.Amount - item.AmountUntaxed;
item.QuantityUnitPriceUntaxed = Math.Round(item.AmountUntaxed / item.Quantity, 4);
item.QuantityUnitPrice = Math.Round(item.Amount / item.Quantity, 4);
applyDetails.Add(item);
}
}
#endregion
return applyDetails;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances
{
public static class PayExtension
{
/// <summary>
/// 使用单笔到账 付款登记
/// </summary>
/// <param name="pay"></param>
/// <param name="payment"></param>
/// <param name="payerAccount"></param>
/// <param name="payeeAccount"></param>
/// <returns></returns>
public static Payment Paying(this Pay pay, Payment payment, Account payerAccount, Account payeeAccount, decimal amount = 0)
{
pay.ThrowIfNull("未传入付款登记信息!");
(pay.AmountPayment >= pay.AmountSettlement).ThrowIfTrue("已完成付款登记,无需重复登记!");
payment.ThrowIfNull("未传入到账付款信息!");
payerAccount.ThrowIfNull("请传入付款账户!");
payeeAccount.ThrowIfNull("请传入收款账户!");
(payment.Currency != pay.Currency).ThrowIfTrue("存在不相符的货币单位!");
if (payment.Type != PaymentType.Split && payment.Type != PaymentType.Pos && payment.Type != PaymentType.Cash && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat) throw new Exception("请选择正确的付款方式!");
if (amount <= 0 || amount > pay.AmountSettlement - pay.AmountPayment) amount = pay.AmountSettlement - pay.AmountPayment;//付款单所需付款的总金额
var amountCanUsed = payment.Amount - payment.AmountUsed;//到账可使用金额
(amountCanUsed < amount).ThrowIfTrue($"付款编号[{payment.SerialNumber}]余额不足!可用余额:{amountCanUsed}");
pay.AmountPayment += amount;
pay.Amount = pay.AmountPayment - pay.AmountDiscount;
pay.PayedTime = payment.OperateTime;
var dynamicSplitPaymentKvid = Guid.NewGuid();
#region 生成DynamicSplit类型的子payment(Biz记录Pay),调用paymentAccept方法 同时对收付账户进行变化
var rtns = new Payment();
rtns.OwnerKvid = payment.OwnerKvid;
rtns.OwnerName = payment.OwnerName;
rtns.Kvid = dynamicSplitPaymentKvid;
rtns.RootKvid = payment.RootKvid;
rtns.ParentKvid = payment.Kvid;
rtns.BizId = pay.SerialNumber;
rtns.BizKvid = pay.Kvid;
rtns.BizType = typeof(Pay).FullName;
rtns.Type = PaymentType.DynamicSplit;
//payment直接用于消费 PayerAccount记录Balance
rtns.PayerKvid = payment.PayerKvid;
rtns.PayerName = payment.PayerName;
rtns.PayerAccountKvid = payerAccount.Kvid;
rtns.PayerAccountName = payerAccount.Name;
rtns.PayerAccountOwnerName = payerAccount.OwnerName;
rtns.PayerAccountSerialNumber = payerAccount.SerialNumber;
//Payee为系统内部的默认业务账户收款
rtns.PayeeKvid = payment.PayeeKvid;
rtns.PayeeName = payment.PayeeName;
rtns.PayeeAccountKvid = payeeAccount.Kvid;
rtns.PayeeAccountName = payeeAccount.Name;
rtns.PayeeAccountOwnerName = payeeAccount.OwnerName;
rtns.PayeeAccountSerialNumber = payeeAccount.SerialNumber;
rtns.Amount = amount;
rtns.AmountUsed = amount;
rtns.AmountSplited = amount;//已消费了,就不能再拆分了 所以已拆分金额要填充上
rtns.OperateTime = DateTime.Now;
rtns.OperatorKvid = KiviiContext.CurrentMember.Kvid;
rtns.OperatorName = KiviiContext.CurrentMember.FullName;
rtns.Currency = pay.Currency;
#endregion
return rtns;
}
/// <summary>
/// 使用单个账户 付款登记
/// </summary>
/// <param name="pay"></param>
/// <param name="payerAccount"></param>
/// <param name="payeeAccount"></param>
/// <returns></returns>
public static Payment Paying(this Pay pay, Account payerAccount, Account payeeAccount, decimal amount = 0)
{
pay.ThrowIfNull("未传入付款登记信息!");
(pay.AmountPayment >= pay.AmountSettlement).ThrowIfTrue("已完成付款登记,无需重复登记!");
payerAccount.ThrowIfNull("请传入付款账户!");
payeeAccount.ThrowIfNull("请传入收款账户!");
(payerAccount.Currency != pay.Currency).ThrowIfTrue("存在不相符的货币单位!");
(payeeAccount.Currency != pay.Currency).ThrowIfTrue("存在不相符的货币单位!");
(payerAccount.Type != AccountType.Deposit && payerAccount.Type != AccountType.Discount).ThrowIfTrue("请选择正确的付款账户!");
if (amount <= 0 || amount > pay.AmountSettlement - pay.AmountPayment) amount = pay.AmountSettlement - pay.AmountPayment;//付款单所需付款的总金额
var amountCanUsed = payerAccount.Amount + payerAccount.CreditLine;//账户可使用金额
(amountCanUsed < amount).ThrowIfTrue($"账户[{payerAccount.Name}]余额不足!可用余额:{amountCanUsed}");
pay.AmountPayment += amount;
pay.Amount = pay.AmountPayment - pay.AmountDiscount;
pay.PayedTime = DateTime.Now;
var dynamicSplitPaymentKvid = Guid.NewGuid();
#region 生成DynamicSplit类型的子payment(Biz记录Pay),调用paymentAccept方法 同时对收付账户进行变化
var rtns = new Payment();
rtns.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
rtns.OwnerName = KiviiContext.CurrentMember.DepartmentName;
rtns.Kvid = dynamicSplitPaymentKvid;
rtns.RootKvid = dynamicSplitPaymentKvid;
rtns.ParentKvid = Guid.Empty;
rtns.BizId = pay.SerialNumber;
rtns.BizKvid = pay.Kvid;
rtns.BizType = typeof(Pay).FullName;
rtns.Type = PaymentType.DynamicSplit;
//payment直接用于消费 PayerAccount记录Balance
rtns.PayerKvid = pay.PayerKvid;
rtns.PayerName = pay.PayerName;
rtns.PayerAccountKvid = payerAccount.Kvid;
rtns.PayerAccountName = payerAccount.Name;
rtns.PayerAccountOwnerName = payerAccount.OwnerName;
rtns.PayerAccountSerialNumber = payerAccount.SerialNumber;
//Payee为系统内部的默认业务账户收款
rtns.PayeeKvid = pay.PayeeKvid;
rtns.PayeeName = pay.PayeeName;
rtns.PayeeAccountKvid = payeeAccount.Kvid;
rtns.PayeeAccountName = payeeAccount.Name;
rtns.PayeeAccountOwnerName = payeeAccount.OwnerName;
rtns.PayeeAccountSerialNumber = payeeAccount.SerialNumber;
rtns.Amount = amount;
rtns.AmountUsed = amount;
rtns.AmountSplited = amount;//已消费了,就不能再拆分了 所以已拆分金额要填充上
rtns.OperateTime = DateTime.Now;
rtns.OperatorKvid = KiviiContext.CurrentMember.Kvid;
rtns.OperatorName = KiviiContext.CurrentMember.FullName;
rtns.Currency = pay.Currency;
#endregion
return rtns;
}
/// <summary>
/// 付款登记折扣金额记录
/// </summary>
/// <param name="pay"></param>
/// <param name="discountAccount"></param>
/// <param name="amount"></param>
/// <param name="remark"></param>
/// <returns></returns>
public static Discount Discount(this Pay pay, Account discountAccount, decimal amount, string remark)
{
pay.ThrowIfNull("未传入付款登记信息!");
discountAccount.ThrowIfNull("请传入收款账户!");
(discountAccount.Type != AccountType.Discount).ThrowIfTrue("请选择正确的折扣账户!");
(discountAccount.Currency != pay.Currency).ThrowIfTrue("存在不相符的货币单位!");
(amount <= 0).ThrowIfTrue("抹零抵扣金额不能少于0!");
(amount > pay.AmountSettlement - pay.AmountPayment).ThrowIfTrue("折扣金额超过付款登记金额!");
pay.AmountPayment += amount;
pay.AmountDiscount = amount;
pay.Amount = pay.AmountPayment - pay.AmountDiscount;
pay.Remark = remark;
#region 记录折扣抹零操作
var rtns = new Discount();
rtns.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
rtns.OwnerName = KiviiContext.CurrentMember.DepartmentName;
rtns.PayKvid = pay.Kvid;
rtns.AccountKvid = discountAccount.Kvid;
rtns.AccountName = discountAccount.Name;
rtns.AccountSerialNumber = discountAccount.SerialNumber;
rtns.Amount = amount;
rtns.Currency = pay.Currency;
rtns.OperateTime = DateTime.Now;
rtns.OperatorKvid = KiviiContext.CurrentMember.Kvid;
rtns.OperatorName = KiviiContext.CurrentMember.FullName;
rtns.Summary = $"{rtns.OperatorName}{rtns.OperateTime}{pay.PayerName}[{pay.SerialNumber}]进行抹零折扣操作,金额为:{amount} {pay.Currency}";
rtns.Remark = remark;
#endregion
return rtns;
}
/// <summary>
/// 作废
/// </summary>
/// <param name="pay"></param>
/// <param name="remark"></param>
/// <returns></returns>
public static Pay Offset(this Pay pay, string remark)
{
pay.ThrowIfNull("付款记录不能为空!");
(pay.Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位");
(pay.OffsetKvid != Guid.Empty).ThrowIfTrue("收项已冲账");
#region payment冲账
var newPayKvid = Guid.NewGuid();
pay.OffsetKvid = newPayKvid;
pay.AddOnlyProperties(o => o.OffsetKvid);
pay.Remark = $"{remark}";
pay.AddOnlyProperties(o => o.Remark);
//新建一个和原来一样的
var offsetPay = new Pay();
offsetPay.PopulateWith(pay);
offsetPay.OffsetKvid = pay.Kvid;//新的冲帐关联用原来的Kvid
offsetPay.Kvid = newPayKvid;
offsetPay.SerialNumber = pay.SerialNumber.IsNullOrEmpty() ? "" : ("-" + pay.SerialNumber);
offsetPay.Remark = $"{remark}";
offsetPay.AmountDiscount = 0 - offsetPay.AmountDiscount;
offsetPay.AmountPayment = 0 - offsetPay.AmountPayment;
offsetPay.AmountSettlement = 0 - offsetPay.AmountSettlement;
offsetPay.Amount = 0 - offsetPay.Amount;//反向值
#endregion
return offsetPay;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances
{
public static class PaymentExtension
{
/// <summary>
/// 获取子付款记录的流水号
/// </summary>
/// <param name="payment"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static string GetSubSerialNumber(this Payment payment, IDbConnection conn = null)
{
payment.RootKvid.ThrowIfEmpty("RootKvid不能为空");
if (conn == null) conn = KiviiContext.GetOpenedDbConnection<Payment>();
//var rootPayment = conn.SingleById<Payment>(payment.RootKvid);
//rootPayment.ThrowIfNull("主付款记录不存在");
var total = conn.Count<Payment>(o => o.RootKvid == payment.RootKvid);
if (total == 0) return string.Empty;
//allcount不需要加1,因为Root的也统计进去了
return $"{payment.SerialNumber}-{total}";
}
/// <summary>
/// 重新计算Payment的 AmountUsed
/// </summary>
/// <param name="payment"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static Payment RecalculateAmountUsed(this Payment payment,IDbConnection conn = null)
{
payment.ThrowIfNull("要更新的付款记录不能为空!");
bool useTransaction = conn == null;//是否启用事务,如果外部未传入conn,启用内部事务
if (conn == null)
{
conn = KiviiContext.GetOpenedDbConnection<Payment>();
}
//var amountInvoiced = conn.Scalar<Payment, decimal>(o => Sql.Sum(o.AmountInvoice), p => p.ParentKvid == payment.Kvid && p.OffsetKvid == Guid.Empty);
var amountUsed = conn.Scalar<Payment, decimal>(o => Sql.Sum(o.AmountUsed), p => p.ParentKvid == payment.Kvid && p.OffsetKvid == Guid.Empty);
Payment parentPayment = null;
if (payment.ParentKvid != Guid.Empty) parentPayment = conn.SingleById<Payment>(payment.ParentKvid);
IDbTransaction trans = null;//事务,如果外部来了Connection,不生成事务
if (useTransaction) trans = conn.OpenTransaction();
try
{
if (payment.OffsetKvid == Guid.Empty)
{
//payment.AmountInvoice = amountInvoiced;
//payment.AddOnlyProperties(o => o.AmountInvoice);
payment.AmountUsed = amountUsed;
payment.AddOnlyProperties(o => o.AmountUsed);
conn.UpdateOnly(payment);
}
if (parentPayment != null) parentPayment.RecalculateAmountUsed(conn);
trans?.Commit();
return payment;
}
catch (Exception ex)
{
trans?.Rollback();
throw ex;
}
}
/// <summary>
/// 重新计算Payment的 AmountInvoice
/// 只针对Bank等根级Payment
/// </summary>
/// <param name="payment"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static Payment RecalculateAmountInvoice(this Payment payment, IDbConnection conn = null)
{
payment.ThrowIfNull("要更新的付款记录不能为空!");
(payment.OffsetKvid != Guid.Empty).ThrowIfTrue("此到账已作废 无法重新计算开票金额");
(payment.RootKvid != payment.Kvid).ThrowIfTrue("不支持的类型");
bool useTransaction = conn == null;//是否启用事务,如果外部未传入conn,启用内部事务
if (conn == null) conn = KiviiContext.GetOpenedDbConnection<Payment>();
var amountInvoiced = conn.Scalar<Payment, decimal>(o => Sql.Sum(o.AmountInvoice), p => p.ParentKvid == payment.Kvid && p.OffsetKvid == Guid.Empty);
IDbTransaction trans = null;//事务,如果外部来了Connection,不生成事务
if (useTransaction) trans = conn.OpenTransaction();
try
{
payment.AmountInvoice = amountInvoiced;
payment.AddOnlyProperties(o => o.AmountInvoice);
conn.UpdateOnly(payment);
trans?.Commit();
return payment;
}
catch (Exception ex)
{
trans?.Rollback();
throw ex;
}
}
/// <summary>
/// 接受一个从系统外来的新付款记录,收付款账户中增加和减小
/// </summary>
/// <param name="payment"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static List<AccountDetail> Accept(this Payment payment)
{
payment.ThrowIfNull("付款记录不能为空!");
(payment.Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位");
(payment.PayerAccountKvid == Guid.Empty).ThrowIfTrue("付款账户不能为空!");
(payment.PayeeAccountKvid == Guid.Empty).ThrowIfTrue("收款账户不能为空!");
(payment.Amount <= 0).ThrowIfTrue("付款记录发生金额不能少于0!");
(payment.AmountInvoice == 0).ThrowIfFalse("付款记录开票金额不为0!");
(payment.AmountUsed == 0).ThrowIfFalse("付款记录使用金额不为0!");
(payment.AmountSplited == 0).ThrowIfFalse("付款记录拆分不为0!");
(payment.Type != PaymentType.Cash && payment.Type != PaymentType.Pos && payment.Type != PaymentType.Bank).ThrowIfTrue("仅支持接受现金刷卡或者银行流水记录");
if (payment.ParentKvid != Guid.Empty) payment.ParentKvid = Guid.Empty;
if (payment.OffsetKvid != Guid.Empty) payment.OffsetKvid = Guid.Empty;
if (payment.Kvid == Guid.Empty) payment.Kvid = Guid.NewGuid();
if (payment.RootKvid != payment.Kvid) payment.RootKvid = payment.Kvid;
var rtns = new List<AccountDetail>();
#region 付款账户变动
var payerAccountDetail = new AccountDetail();
payerAccountDetail.AccountKvid = payment.PayerAccountKvid;
payerAccountDetail.BizId = payment.SerialNumber;
payerAccountDetail.BizKvid = payment.Kvid;
payerAccountDetail.BizType = typeof(Payment).FullName;
payerAccountDetail.PayerKvid = payment.PayerKvid;
payerAccountDetail.PayerName = payment.PayerName;
payerAccountDetail.PayerAccountKvid = payment.PayerAccountKvid;
payerAccountDetail.PayerAccountName = payment.PayerAccountName;
payerAccountDetail.PayerAccountSerialNumber = payment.PayerAccountSerialNumber;
payerAccountDetail.PayeeKvid = payment.PayeeKvid;
payerAccountDetail.PayeeName = payment.PayeeName;
payerAccountDetail.PayeeAccountKvid = payment.PayeeAccountKvid;
payerAccountDetail.PayeeAccountName = payment.PayeeAccountName;
payerAccountDetail.PayeeAccountSerialNumber = payment.PayeeAccountSerialNumber;
payerAccountDetail.AmountPayment = payment.Amount;
payerAccountDetail.Summary = payment.Summary;
payerAccountDetail.Summary = payment.Summary;
payerAccountDetail.Remark = payment.Remark;
rtns.Add(payerAccountDetail);
#endregion
#region 收款账户变动
var payeeAccountDetail = new AccountDetail();
payeeAccountDetail.AccountKvid = payment.PayeeAccountKvid;
payeeAccountDetail.BizId = payment.SerialNumber;
payeeAccountDetail.BizKvid = payment.Kvid;
payeeAccountDetail.BizType = typeof(Payment).FullName;
payeeAccountDetail.PayerKvid = payment.PayerKvid;
payeeAccountDetail.PayerName = payment.PayerName;
payeeAccountDetail.PayerAccountKvid = payment.PayerAccountKvid;
payeeAccountDetail.PayerAccountName = payment.PayerAccountName;
payeeAccountDetail.PayerAccountSerialNumber = payment.PayerAccountSerialNumber;
payeeAccountDetail.PayeeKvid = payment.PayeeKvid;
payeeAccountDetail.PayeeName = payment.PayeeName;
payeeAccountDetail.PayeeAccountKvid = payment.PayeeAccountKvid;
payeeAccountDetail.PayeeAccountName = payment.PayeeAccountName;
payeeAccountDetail.PayeeAccountSerialNumber = payment.PayeeAccountSerialNumber;
payeeAccountDetail.AmountPayment = payment.Amount;
payeeAccountDetail.Summary = payment.Summary;
payeeAccountDetail.Summary = payment.Summary;
payeeAccountDetail.Remark = payment.Remark;
rtns.Add(payeeAccountDetail);
#endregion
return rtns;
}
/// <summary>
/// 到账拆分,并且更新原payment.AmountSplit字段
/// </summary>
/// <param name="payment"></param>
/// <param name="amountSplit"></param>
/// <param name="ownerKvid"></param>
/// <param name="ownerName"></param>
/// <returns></returns>
public static Payment BizSplit(this Payment payment, decimal amountSplit, Guid? ownerKvid = null, string ownerName = null)
{
payment.ThrowIfNull($"传入payment参数为空");
if (payment.Type != PaymentType.Bank) throw new Exception("非银行到账不可以认领!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("已冲销收款不可认领!");
(amountSplit <= 0).ThrowIfTrue("拆分认领金额不能为零或负值。");
if (payment.AmountSplited == payment.Amount) throw new Exception("无剩余收款可认领!");
if (payment.AmountSplited + amountSplit > payment.Amount) throw new Exception("认领金额超出范围!");
#region 新增拆分项目
var splitPayment = new Payment();
splitPayment.PopulateWith(payment);//先从父级中拷贝所有参数
//赋值
splitPayment.Kvid = Guid.NewGuid();
splitPayment.ParentKvid = payment.Kvid;
splitPayment.RootKvid = payment.RootKvid;
splitPayment.OffsetKvid = Guid.Empty;
splitPayment.OwnerKvid = ownerKvid == null ? Guid.Empty : ownerKvid.Value;
splitPayment.OwnerName = ownerName;
//业务设置
splitPayment.BizId = string.Empty;
splitPayment.BizKvid = Guid.Empty;
splitPayment.BizType = string.Empty;
splitPayment.SerialNumber = payment.GetSubSerialNumber();
splitPayment.Type = PaymentType.Split;
//金额设置
splitPayment.Amount = amountSplit;
splitPayment.AmountSplited = 0;
splitPayment.AmountUsed = 0;
splitPayment.AmountInvoice = payment.AmountInvoice > amountSplit ? amountSplit : payment.AmountInvoice;
splitPayment.Summary = string.Empty;
splitPayment.Remark = string.Empty;
splitPayment.OperateTime = payment.OperateTime;
splitPayment.OperatorName = KiviiContext.CurrentMember.FullName;
splitPayment.OperatorKvid = KiviiContext.CurrentMember.Kvid;
//splitPayment.Status = 1;
#endregion
#region 更新主项字段
payment.AmountSplited += amountSplit;
payment.AddOnlyProperties(o => o.AmountSplited);
#endregion
return splitPayment;
}
/// <summary>
/// 非业务收款拆分处理,非业务收款或退款
/// </summary>
/// <param name="payment"></param>
/// <param name="amountSplit"></param>
/// <param name="ownerKvid"></param>
/// <param name="ownerName"></param>
/// <returns></returns>
public static Payment UnBizSplit(this Payment payment, decimal amountSplit, PaymentType paymentType)
{
payment.ThrowIfNull($"传入payment参数为空");
if (payment.Type != PaymentType.Bank) throw new Exception("非银行到账不可以操作!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("已冲销收款不可操作!");
(amountSplit <= 0).ThrowIfTrue("操作金额不能为零或负值。");
if (payment.AmountSplited == payment.Amount) throw new Exception("无剩余收款可操作!");
if (payment.AmountSplited + amountSplit > payment.Amount) throw new Exception("操作金额超出范围!");
if (paymentType != PaymentType.UnBiz && paymentType != PaymentType.Refund) throw new Exception("此方法只支持非业务拆分或退款拆分!");
#region 新增拆分项目
var splitPayment = new Payment();
splitPayment.PopulateWith(payment);//先从父级中拷贝所有参数
//赋值
splitPayment.Kvid = Guid.NewGuid();
splitPayment.ParentKvid = payment.Kvid;
splitPayment.RootKvid = payment.RootKvid;
splitPayment.OffsetKvid = Guid.Empty;
splitPayment.OwnerKvid = Guid.Empty;
splitPayment.OwnerName = string.Empty;
//业务设置
splitPayment.BizId = string.Empty;
splitPayment.BizKvid = Guid.Empty;
splitPayment.BizType = string.Empty;
splitPayment.SerialNumber = payment.GetSubSerialNumber();
splitPayment.Type = paymentType;
//金额设置
splitPayment.Amount = amountSplit;
splitPayment.AmountSplited = 0;
splitPayment.AmountUsed = 0;
splitPayment.AmountInvoice = 0;
splitPayment.Summary = string.Empty;
splitPayment.Remark = string.Empty;
splitPayment.OperateTime = payment.OperateTime;
splitPayment.OperatorName = KiviiContext.CurrentMember.FullName;
splitPayment.OperatorKvid = KiviiContext.CurrentMember.Kvid;
//splitPayment.Status = 1;
#endregion
#region 更新主项字段
payment.AmountSplited += amountSplit;
payment.AddOnlyProperties(o => o.AmountSplited);
#endregion
return splitPayment;
}
/// <summary>
/// 到账冲账,此方法不做业务判断,传过来的Payment均可Offset,除已经冲账过的
/// </summary>
/// <param name="payment"></param>
/// <param name="remark"></param>
/// <returns></returns>
public static Payment Offset(this Payment payment, string remark)
{
payment.ThrowIfNull("付款记录不能为空!");
(payment.Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位");
(payment.OffsetKvid != Guid.Empty).ThrowIfTrue("收项已冲账");
#region payment冲账
var newPaymentKvid = Guid.NewGuid();
payment.OffsetKvid = newPaymentKvid;
payment.AddOnlyProperties(o => o.OffsetKvid);
payment.Remark = $"{remark}";
payment.AddOnlyProperties(o => o.Remark);
//新建一个和原来一样的
var offsetPayment = new Payment();
offsetPayment.PopulateWith(payment);
offsetPayment.OffsetKvid = payment.Kvid;//新的冲帐关联用原来的Kvid
offsetPayment.Kvid = newPaymentKvid;
offsetPayment.SerialNumber = payment.SerialNumber.IsNullOrEmpty() ? "" : ("-" + payment.SerialNumber);
offsetPayment.Remark = $"{remark}";
offsetPayment.Amount = 0 - offsetPayment.Amount;//反向值
#endregion
return offsetPayment;
}
/// <summary>
/// 到账转账至账户
/// </summary>
/// <param name="payment"></param>
/// <param name="accountPayee"></param>
/// <param name="accountPayer"></param>
/// <param name="amount"></param>
/// <param name="remark"></param>
/// <returns></returns>
public static Payment Transfer(this Payment payment, Account accountPayee, Account accountPayer, decimal amount, string remark)
{
payment.ThrowIfNull("到账不能为空!");
accountPayee.ThrowIfNull("账户不能为空!");
accountPayer.ThrowIfNull("账户不能为空!");
(amount <= 0).ThrowIfTrue("入账金额不能小于等0!");
(payment.OffsetKvid != Guid.Empty).ThrowIfTrue("此项已冲账!");
if (payment.Amount - payment.AmountUsed < amount) throw new Exception($"此到账可使用金额不足,仅剩:{payment.Amount - payment.AmountUsed}元");
(payment.Currency != accountPayee.Currency && accountPayee.Currency != accountPayer.Currency).ThrowIfTrue("账户的货币单位不一致!");
var transferPaymentKvid = Guid.NewGuid();
#region 创建入账Payment
var transferPayment = new Payment();
transferPayment.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
transferPayment.OwnerName = KiviiContext.CurrentMember.DepartmentName;
transferPayment.Kvid = transferPaymentKvid;
transferPayment.RootKvid = payment.RootKvid;
transferPayment.ParentKvid = payment.Kvid;
transferPayment.Type = PaymentType.Transfer;
transferPayment.BizId = accountPayee.Name;
transferPayment.BizKvid = accountPayee.Kvid;
transferPayment.BizType = typeof(Account).FullName;
transferPayment.PayerName = payment.PayerName;
transferPayment.PayerKvid = payment.PayerKvid;
transferPayment.PayerAccountName = accountPayer.Name;
transferPayment.PayerAccountKvid = accountPayer.Kvid;
transferPayment.PayerAccountOwnerName = accountPayer.OwnerName;
transferPayment.PayerAccountSerialNumber = accountPayer.SerialNumber;
transferPayment.PayeeName = accountPayee.Name;
transferPayment.PayeeKvid = accountPayee.Kvid;
transferPayment.PayeeAccountName = accountPayee.Name;
transferPayment.PayeeAccountKvid = accountPayee.Kvid;
transferPayment.PayeeAccountOwnerName = accountPayee.OwnerName;
transferPayment.PayeeAccountSerialNumber = accountPayee.SerialNumber;
transferPayment.Amount = amount;
transferPayment.AmountSplited = 0;
transferPayment.AmountUsed = amount;
transferPayment.OperateTime = DateTime.Now;
transferPayment.OperatorName = KiviiContext.CurrentMember.FullName;
transferPayment.OperatorKvid = KiviiContext.CurrentMember.Kvid;
transferPayment.Currency = payment.Currency;
transferPayment.Summary = $"{remark}";
transferPayment.Remark = $"{remark}";
#endregion
payment.AmountUsed += amount;
payment.AddOnlyProperties(o => o.AmountSplited);
return transferPayment;
}
/// <summary>
/// Payment与Settlement产生关联的方法
/// </summary>
/// <param name="payments"></param>
/// <param name="settlements"></param>
/// <returns></returns>
public static List<Payment> Correlating(this List<Payment> payments,List<Settlement> settlements)
{
payments.ThrowIfNullOrEmpty("要关联的Payments不能为空!");
settlements.ThrowIfNullOrEmpty("要关联的Settlements不能为空!");
payments.Exists(o => o.Type != PaymentType.DynamicSplit).ThrowIfTrue("请选择正确收款类型进行关联结算");
var currentPayments = new List<Payment>();//不使用传入的List 防止修改了原List的数据
currentPayments.AddRange(payments);
var rtns = new List<Payment>();
foreach (var settlement in settlements)
{
//说明已经建立关联关系的settlement
if (settlement.AmountPayment >= settlement.Amount) continue;
foreach (var payment in currentPayments)
{
var settlementAmount = settlement.Amount - settlement.AmountPayment;
if (settlementAmount <= 0) break;
if (payment.Amount <= 0) continue;
var amount = settlementAmount <= payment.Amount ? settlementAmount : payment.Amount;
var payPayment = new Payment();
payPayment.PopulateInstance(payment);
payPayment.SerialNumber = string.Empty;
payPayment.Kvid = Guid.NewGuid();
payPayment.ParentKvid = payment.Kvid;
payPayment.BizId = settlement.SerialNumber;
payPayment.BizType = typeof(Settlement).FullName;
payPayment.BizKvid = settlement.Kvid;
payPayment.Type = PaymentType.Pay;
payPayment.Amount = amount;
payPayment.AmountUsed = amount;
payPayment.AmountSplited = amount;
rtns.Add(payPayment);
payment.Amount -= amount;
settlement.AmountPayment += amount;
}
settlement.AddOnlyProperties(o => o.AmountPayment);
}
return rtns;
}
/// <summary>
/// 到账 支付或充值 调用方法
/// </summary>
/// <param name="payment"></param>
/// <param name="payerAccount"></param>
/// <param name="payeeAccount"></param>
/// <returns></returns>
internal static List<AccountDetail> Using(this Payment payment, Account payerAccount, Account payeeAccount, string remark = null)
{
payment.ThrowIfNull("付款记录不能为空!");
payerAccount.ThrowIfNull("未找到付款账户!");
payeeAccount.ThrowIfNull("未找到收款账户!");
(payment.Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位");
(payment.Type == PaymentType.DynamicSplit || payment.Type == PaymentType.Transfer).ThrowIfFalse("此方法仅支持支付或充值");
(payment.Amount <= 0).ThrowIfTrue("付款记录发生金额不能少于0!");
(payment.PayerAccountKvid == Guid.Empty || payment.PayeeAccountKvid == Guid.Empty).ThrowIfTrue("收付款账户不能为空!");
(payment.AmountUsed != payment.Amount).ThrowIfTrue("已使用金额不等于支付金额!");
//(payment.PayerAccountKvid != payerAccount.Kvid).ThrowIfTrue("传入付款账户不一致!");
//(payment.PayeeAccountKvid != payeeAccount.Kvid).ThrowIfTrue("传入收款账户不一致!");
(payerAccount.Currency != payment.Currency).ThrowIfTrue($"付款账户({payerAccount.Currency})与此付款({payment.Currency})的货币单位不一致!");
(payeeAccount.Currency != payment.Currency).ThrowIfTrue($"收款账户({payeeAccount.Currency})与此付款({payment.Currency})的货币单位不一致!");
var rtns = new List<AccountDetail>();
#region 付款账户变动
var payerAccountDetail = new AccountDetail();
payerAccountDetail.AccountKvid = payerAccount.Kvid;
payerAccountDetail.BizId = payment.SerialNumber;
payerAccountDetail.BizKvid = payment.Kvid;
payerAccountDetail.BizType = typeof(Payment).FullName;
payerAccountDetail.PayerKvid = payment.PayerKvid;
payerAccountDetail.PayerName = payment.PayerName;
payerAccountDetail.PayerAccountKvid = payerAccount.Kvid;
payerAccountDetail.PayerAccountName = payerAccount.Name;
payerAccountDetail.PayerAccountSerialNumber = payerAccount.SerialNumber;
payerAccountDetail.PayeeKvid = payment.PayeeKvid;
payerAccountDetail.PayeeName = payment.PayeeName;
payerAccountDetail.PayeeAccountKvid = payeeAccount.Kvid;
payerAccountDetail.PayeeAccountName = payeeAccount.Name;
payerAccountDetail.PayeeAccountSerialNumber = payeeAccount.SerialNumber;
payerAccountDetail.AmountPayment = payment.Amount;
payerAccountDetail.Summary = payment.Summary;
payerAccountDetail.Remark = remark;
rtns.Add(payerAccountDetail);
#endregion
#region 收款账户变动
var payeeAccountDetail = new AccountDetail();
payeeAccountDetail.AccountKvid = payeeAccount.Kvid;
payeeAccountDetail.BizId = payment.SerialNumber;
payeeAccountDetail.BizKvid = payment.Kvid;
payeeAccountDetail.BizType = typeof(Payment).FullName;
payeeAccountDetail.PayerKvid = payment.PayerKvid;
payeeAccountDetail.PayerName = payment.PayerName;
payeeAccountDetail.PayerAccountKvid = payerAccount.Kvid;
payeeAccountDetail.PayerAccountName = payerAccount.Name;
payeeAccountDetail.PayerAccountSerialNumber = payerAccount.SerialNumber;
payeeAccountDetail.PayeeKvid = payment.PayeeKvid;
payeeAccountDetail.PayeeName = payment.PayeeName;
payeeAccountDetail.PayeeAccountKvid = payeeAccount.Kvid;
payeeAccountDetail.PayeeAccountName = payeeAccount.Name;
payeeAccountDetail.PayeeAccountSerialNumber = payeeAccount.SerialNumber;
payeeAccountDetail.AmountPayment = payment.Amount;
payeeAccountDetail.Summary = payment.Summary;
payeeAccountDetail.Remark = remark;
rtns.Add(payeeAccountDetail);
#endregion
return rtns;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8D9479E9-5C4F-4D96-8BC1-C771BE0D91B1}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Kivii.Finances</RootNamespace>
<AssemblyName>Kivii.Biz.Finances.V4.5</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Kivii.Common.V4.5, Version=5.6.2021.10000, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Kivii.Common.5.6.2021.10000\lib\net45\Kivii.Common.V4.5.dll</HintPath>
</Reference>
<Reference Include="Kivii.Core.V4.5, Version=5.6.2021.10000, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Kivii.Core.5.6.2021.10000\lib\net45\Kivii.Core.V4.5.dll</HintPath>
</Reference>
<Reference Include="Kivii.Linq.V4.5, Version=5.6.2021.10190, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Kivii.Linq.5.6.2021.10190\lib\net45\Kivii.Linq.V4.5.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Configs.cs" />
<Compile Include="Entities\Account.cs" />
<Compile Include="Entities\AccountDetail.cs" />
<Compile Include="Entities\Analysis.cs" />
<Compile Include="Entities\Bill.cs" />
<Compile Include="Entities\BillDetail.cs" />
<Compile Include="Entities\Discount.cs" />
<Compile Include="Entities\Invoice.cs" />
<Compile Include="Entities\InvoiceApply.cs" />
<Compile Include="Entities\InvoiceApplyDetail.cs" />
<Compile Include="Entities\InvoiceDetail.cs" />
<Compile Include="Entities\InvoiceTitle.cs" />
<Compile Include="Entities\Pay.cs" />
<Compile Include="Entities\Payment.cs" />
<Compile Include="Entities\Settlement.cs" />
<Compile Include="Entities\SettlementAnalysis.cs" />
<Compile Include="Entities\SettlementDetail.cs" />
<Compile Include="Entities\SettlementGroup.cs" />
<Compile Include="Entities\Transfer.cs" />
<Compile Include="Extensions\AccountExtension.cs" />
<Compile Include="Extensions\BillExtension.cs" />
<Compile Include="Extensions\InvoiceApplyExtension.cs" />
<Compile Include="Extensions\PayExtension.cs" />
<Compile Include="Extensions\PaymentExtension.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Transforms\RestfulAccount.cs" />
<Compile Include="Transforms\RestfulAccountDetail.cs" />
<Compile Include="Transforms\RestfulBill.cs" />
<Compile Include="Transforms\RestfulInvoice.Debit.cs" />
<Compile Include="Transforms\RestfulInvoiceApply.Apply.cs" />
<Compile Include="Transforms\RestfulPay.cs" />
<Compile Include="Transforms\RestfulPayment.Accept.cs" />
<Compile Include="Transforms\RestfulPayment.Account.cs" />
<Compile Include="Transforms\RestfulPayment.cs" />
<Compile Include="Transforms\RestfulPayment.Offset.cs" />
<Compile Include="Transforms\RestfulPayment.Split.cs" />
<Compile Include="Transforms\RestfulPayment.UnBiz.Refund.cs" />
<Compile Include="Transforms\RestfulSettlement.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
</Project>
\ No newline at end of file
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Kivii.Biz.Finances.V4.5")]
[assembly: AssemblyDescription("财务管理模块")]
[assembly: AssemblyDefaultAlias("Kivii.Finances")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Kivii.Biz.Finances.V4.5")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("8d9479e9-5c4f-4d96-8bc1-c771be0d91b1")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("5.4.2021.09140")]
[assembly: AssemblyFileVersion("5.4.2021.09140")]
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountCreate : RestfulCreate<Account>
{
private IDbTransaction _trans;
public override bool OnPreRestfulCreate(IRequest req, IResponse res, IDbConnection dbConnection, IRestfulCreateResponse<Account> rtns)
{
if (Item.OwnerKvid == Guid.Empty) throw new Exception("未指定账户所有人");
if (Item.OwnerName.IsNullOrEmpty()) throw new Exception("未指定账户所有人名称");
if (Item.Currency == CurrencyUnit.Unsupported) throw new Exception("不支持的货币类型!");
if (Item.Type == AccountType.Unsupported || Item.Type == AccountType.Balance || Item.Type == AccountType.Biz || Item.Type == AccountType.Coupon || Item.Type == AccountType.Pos || Item.Type == AccountType.StoredCard || Item.Type == AccountType.Cash) throw new Exception("不支持的账户类型!");
if (Item.Amount != 0) throw new Exception("开户时,户内金额只能为零!");
if (Item.Type == AccountType.Deposit && dbConnection.Exists<Account>(o => o.Name == Item.Name && o.OwnerKvid == Item.OwnerKvid && o.Type == AccountType.Deposit && o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid)) throw new Exception("当前组织已存在此客户的存款账户");
if (Item.Type == AccountType.WeChat && dbConnection.Exists<Account>(o => o.OwnerKvid == Item.OwnerKvid && o.Type == AccountType.WeChat && o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid)) throw new Exception("当前组织已存在微信账户!");
if (Item.Type == AccountType.AliPay && dbConnection.Exists<Account>(o => o.OwnerKvid == Item.OwnerKvid && o.Type == AccountType.AliPay && o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid)) throw new Exception("当前组织已存在支付宝账户!");
if (Item.Type == AccountType.Bank && dbConnection.Exists<Account>(o => o.OwnerKvid == Item.OwnerKvid && o.Type == AccountType.Bank && o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid)) throw new Exception("当前组织已存在银行账户!");
_trans = dbConnection.OpenTransaction();
return base.OnPreRestfulCreate(req, res, dbConnection, rtns);
}
public override bool OnPostRestfulCreate(IRequest req, IResponse res, IDbConnection dbConnection, IRestfulCreateResponse<Account> rtns)
{
//新建开户记录
var accountDetail = new AccountDetail();
accountDetail.AccountKvid = Item.Kvid;
accountDetail.BizKvid = Item.Kvid;
accountDetail.BizType = typeof(Account).FullName;
accountDetail.BizId = Item.SerialNumber;
accountDetail.PayerKvid = Item.Kvid;
accountDetail.PayerName = Item.Name;
accountDetail.PayerAccountKvid = Item.Kvid;
accountDetail.PayerAccountName = Item.Name;
accountDetail.PayerAccountSerialNumber = Item.SerialNumber;
accountDetail.PayeeKvid = Item.Kvid;
accountDetail.PayeeName = Item.Name;
accountDetail.PayeeAccountKvid = Item.Kvid;
accountDetail.PayeeAccountName = Item.Name;
accountDetail.PayeeAccountSerialNumber = Item.SerialNumber;
accountDetail.AmountPayment = 0;
accountDetail.Amount = 0;
accountDetail.Summary = "开户";
accountDetail.Remark = "开户";
dbConnection.Insert(accountDetail);
_trans.Commit();
return base.OnPostRestfulCreate(req, res, dbConnection, rtns);
}
}
[Api(Description = "账户查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountQuery : RestfulExecution<Account>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<Account>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Account>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
sqlExpress.Where(o => o.Type != AccountType.Balance);
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountQueryEx : RestfulQuery<Account>
{
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountRead : RestfulRead<Account>
{
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountUpdate : RestfulUpdate<Account>
{
}
[Api(Description = "获取一个折扣账户")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountGetDiscount : RestfulExecution<Account>
{
public CurrencyUnit Currency { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位!");
var rtns = new RestfulReadResponse<Account>();
rtns.Result = Currency.GetDiscountAccount();
return rtns;
}
}
}
using Kivii.Finances.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountDetailQuery : RestfulQuery<AccountDetail>
{ }
[RequiresAnyRole(SystemRoles.Everyone)]
public class AccountDetailRead : RestfulRead<AccountDetail>
{ }
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "创建账单")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillCreate : RestfulExecution<Bill>
{
public List<Guid> SettlementKvids { get; set; }
public Bill Item { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Item.PayerName.IsNullOrEmpty() || Item.PayeeName.IsNullOrEmpty()) throw new Exception("收付双方不能为空!");
if (Item.Currency == CurrencyUnit.Unsupported) throw new Exception("未传入货币单位!");
if (SettlementKvids.IsNullOrEmpty()) throw new Exception("未传入结算数据");
var conn = KiviiContext.GetOpenedDbConnection<Bill>();
conn.InitEntityType<Bill>();
conn.InitEntityType<BillDetail>();
var settlements = conn.SelectByIds<Settlement>(SettlementKvids);
if (settlements == null) throw new Exception("无对应数据导出!");
(settlements.Exists(o => o.Currency != Item.Currency)).ThrowIfTrue("存在于账单不一致的货币单位不可制作!");
if (Item.Amount != settlements.Sum(o => o.Amount)) throw new Exception("账单金额不等于选择的结算金额之和!");
var rtns = new RestfulCreateResponse<Bill>();
rtns.Results = new List<Bill>();
var trans = conn.OpenTransaction();
try
{
var billKvid = Guid.NewGuid();
Item.Kvid = billKvid;
Item.OperateTime = DateTime.Now;
Item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
Item.OperatorName = KiviiContext.CurrentMember.FullName;
Item.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
Item.OwnerName = KiviiContext.CurrentMember.DepartmentName;
conn.Insert(Item);
rtns.Results.Add(Item);
foreach (var settlement in settlements)
{
var billDetail = new BillDetail();
billDetail.BillKvid = billKvid;
billDetail.BizId = settlement.SerialNumber;
billDetail.BizType = typeof(Settlement).FullName;
billDetail.BizKvid = settlement.Kvid;
billDetail.Type = settlement.Type;
billDetail.Amount = settlement.Amount;
billDetail.Currency = settlement.Currency;
billDetail.Summary = settlement.Summary;
billDetail.Remark = settlement.Remark;
billDetail.OperateTime = DateTime.Now;
billDetail.OperatorKvid = KiviiContext.CurrentMember.Kvid;
billDetail.OperatorName = KiviiContext.CurrentMember.FullName;
conn.Insert(billDetail);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillUpdate : RestfulUpdate<Bill>
{
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillRead : RestfulRead<Bill>
{
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillQuery : RestfulQuery<Bill>
{
/// <summary>
/// true:只查已经归档的账单
/// false:只查未支付的账单
/// 不传入: 全部账单
/// </summary>
public bool? Payed { get; set; }
public override bool OnPreRestfulQuery(IRequest req, IResponse res, IDbConnection dbConnection, IRestfulQueryResponse<Bill> rtns, ref Expression<Func<Bill, bool>> where)
{
if (Payed == null) return base.OnPreRestfulQuery(req, res, dbConnection, rtns, ref where);
if (Payed.Value) where = o => o.Amount == o.AmountPayment;
if (!Payed.Value) where = o => o.Amount > o.AmountPayment;
return base.OnPreRestfulQuery(req, res, dbConnection, rtns, ref where);
}
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillDelete: RestfulDelete<Bill>
{
IDbTransaction _trans = null;
public override bool OnPreRestfulDelete(IRequest req, IResponse res, IDbConnection dbConnection, IRestfulDeleteResponse<Bill> rtns)
{
dbConnection.Exists<Bill>(o => Sql.In(o.Kvid, Kvids) && o.AmountPayment == o.Amount).ThrowIfTrue("此账单已归档付款登记,无法删除");
_trans = dbConnection.OpenTransaction();
return base.OnPreRestfulDelete(req, res, dbConnection, rtns);
}
public override bool OnPostRestfulDelete(IRequest req, IResponse res, IDbConnection dbConnection, IRestfulDeleteResponse<Bill> rtns)
{
try
{
var deleteBillDetails = dbConnection.From<BillDetail>().Update(o => o.Status).Where(o => Sql.In(o.BillKvid, Kvids));
dbConnection.UpdateOnly<BillDetail>(new BillDetail { Status = -1 }, deleteBillDetails);
_trans.Commit();
}
catch (Exception ex)
{
_trans.Rollback();
throw ex;
}
return base.OnPostRestfulDelete(req, res, dbConnection, rtns);
}
}
[Api(Description = "账单归档")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class BillArchiving : RestfulExecution<Bill>
{
public Guid Kvid { get; set; }
public List<Guid> Kvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Kvid == null && Kvids.IsNullOrEmpty()).ThrowIfTrue("请传入要结算的内容!");
if (Kvids.IsNullOrEmpty()) Kvids = new List<Guid>();
if (Kvid != null) Kvids.Add(Kvid);
var conn = KiviiContext.GetOpenedDbConnection<Bill>();
var bills = conn.SelectByIds<Bill>(Kvids);
if (bills.IsNullOrEmpty()) throw new Exception("未找到传入的账单信息!");
var rtns = new RestfulUpdateResponse<Bill>();
rtns.Results = new List<Bill>();
var allBillDetails = conn.Select<BillDetail>(o => Sql.In(o.BillKvid, bills.ConvertAll(p => p.Kvid)));
var allSettlements = conn.Select<Settlement>(o => Sql.In(o.Kvid, allBillDetails.ConvertAll(p => p.BizKvid)));
foreach (var bill in bills)
{
var billDetails = allBillDetails.Where(o => o.BillKvid == bill.Kvid).ToList();
if (billDetails.IsNullOrEmpty()) continue;
var settlements = allSettlements.Where(o => billDetails.ConvertAll(p => p.BizKvid).Contains(o.Kvid)).ToList();
if (settlements.IsNullOrEmpty())//未找到结算信息 说明此账单没有费用
{
bill.Amount = 0;
bill.AddOnlyProperties(o => o.Amount);
bill.AmountPlan = 0;
bill.AddOnlyProperties(o => o.AmountPlan);
bill.AmountPayment = 0;
bill.AddOnlyProperties(o => o.AmountPayment);
conn.UpdateOnly(bill);
rtns.Results.Add(bill);
continue;
}
bill.Amount = settlements.Sum(o => o.Amount);
bill.AddOnlyProperties(o => o.Amount);
bill.AmountPlan = settlements.Sum(o => o.AmountPlan);
bill.AddOnlyProperties(o => o.AmountPlan);
bill.AmountPayment = settlements.Sum(o => o.AmountPayment);
bill.AddOnlyProperties(o => o.AmountPayment);
conn.UpdateOnly(bill);
rtns.Results.Add(bill);
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "借票查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceDebitQuery : RestfulExecution<Invoice>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public DateTime? BeginTime { get; set; }
public DateTime? EndTime { get; set; }
public bool? Debited { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(BeginTime != null && EndTime != null && BeginTime.Value > EndTime.Value).ThrowIfTrue("开始日期不能大于结束时间!");
var conn = KiviiContext.GetOpenedDbConnection<Invoice>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Invoice>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
sqlExpress.Where(o => o.RootKvid == o.Kvid && o.OffsetKvid == Guid.Empty && o.Category == InvoiceApplyType.Debit.ToString());
if (BeginTime != null) sqlExpress.And(o => o.OperateTime >= BeginTime.Value);
if (EndTime != null) sqlExpress.And(o => o.OperateTime < EndTime.Value);
if (Debited != null)
{
if (Debited.Value) sqlExpress.And(o => o.AmountPayment == o.Amount);
else sqlExpress.And(o => o.AmountPayment < o.Amount);
}
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
if (Debited != null)
{
if (!Debited.Value)
{
var queryBankAccount = conn.From<Account>();
queryBankAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Bank, AccountType.WeChat, AccountType.AliPay));
queryBankAccount.Select(o => o.Kvid);
var queryCashAccount = conn.From<Account>();
queryCashAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Cash, AccountType.Pos));
queryCashAccount.Select(o => o.Kvid);
var queryPayment = conn.From<Payment>();
queryPayment.Where(o => o.RootKvid == o.Kvid && o.OffsetKvid == Guid.Empty && o.AmountInvoice < o.Amount && (Sql.In(o.PayeeAccountKvid, queryBankAccount) || Sql.In(o.PayerAccountKvid, queryCashAccount)));
queryPayment.Select(o => o.PayerName);
var paymentNames = conn.Column<string>(queryPayment).Distinct().ToList();
foreach (var item in rtns.Results)
{
if (paymentNames.Exists(o => o == item.PayerName))
{
item.Type = "Debit";
}
}
rtns.Results = rtns.Results.OrderBy(o => o.Type).ToList();
}
}
return rtns;
}
}
[Api(Description = "借票冲销")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceDebiting : RestfulExecution<Invoice>
{
public Guid ApplyKvid { get; set; }
public Guid PaymentKvid { get; set; }
//public AccountType Type { get; set; }
public decimal Amount { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (ApplyKvid == Guid.Empty) throw new Exception("请传入借票申请对象!");
//if (Type != AccountType.Bank && Type != AccountType.Cash && Type != AccountType.Pos) throw new Exception("冲销类型不正确!");
//if (Type != AccountType.Bank && Amount <= 0) throw new Exception("冲销金额不能小于0!");
//if (Type == AccountType.Bank && PaymentKvid == Guid.Empty) throw new Exception("请选择到账进行冲销!");
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var apply = conn.SingleById<InvoiceApply>(ApplyKvid);
apply.ThrowIfNull("未找到所选借票申请数据!");
(apply.OperateType != InvoiceApplyType.Debit).ThrowIfTrue("非借票不可冲销!");
(apply.OffsetKvid != Guid.Empty).ThrowIfTrue("当前借票申请已冲账,无法继续操作!");
(apply.Status != (int)InvoiceApplyStatus.Completed).ThrowIfTrue("当前借票申请未执行完毕,无法冲销!");
var existInvoices = conn.Select<Invoice>(o => o.ApplyKvid == apply.Kvid && o.OffsetKvid == Guid.Empty && o.RootKvid == o.Kvid);
if (existInvoices.IsNullOrEmpty()) throw new Exception("未找到所选申请对应的发票信息!");
var unDebitAmount = existInvoices.Sum(o => o.Amount) - existInvoices.Sum(o => o.AmountPayment);
(unDebitAmount <= 0).ThrowIfTrue("此申请无需再冲销!");
var applyRelations = conn.Select<InvoiceApply>(o => o.ParentKvid == apply.Kvid && o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);
var rtns = new RestfulUpdateResponse<Invoice>();
rtns.Results = new List<Invoice>();
var payment = conn.SingleById<Payment>(PaymentKvid);
if (payment.Type != PaymentType.Bank && payment.Type != PaymentType.Cash && payment.Type != PaymentType.Pos && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat) throw new Exception("请选择正确银行到账进行冲销!");
if (payment.AmountInvoice >= payment.Amount) throw new Exception("当前到账已开票,无法冲销当前借票!");
var splitPayments = conn.Select<Payment>(o => o.ParentKvid == payment.Kvid && o.OffsetKvid == Guid.Empty && o.Type == PaymentType.Split);
if (Amount <= 0 || Amount > (payment.Amount - payment.AmountInvoice)) Amount = payment.Amount - payment.AmountInvoice;
decimal amount = Amount >= unDebitAmount ? unDebitAmount : Amount;
var relation = new InvoiceApply();//apply关联Payment
relation.PopulateWith(apply);
relation.Kvid = Guid.NewGuid();
relation.RootKvid = apply.Kvid;
relation.ParentKvid = apply.Kvid;
relation.SerialNumber = string.Empty;
relation.OperateType = InvoiceApplyType.Related;
relation.OwnerKvid = apply.OwnerKvid;
relation.OwnerName = apply.OwnerName;
relation.BizId = payment.SerialNumber;
relation.BizKvid = payment.Kvid;
relation.BizType = typeof(Payment).FullName;
relation.Type = apply.OperateType.ToString();
relation.Amount = amount;
relation.OperatorName = KiviiContext.CurrentMember.FullName;
relation.OperatorKvid = KiviiContext.CurrentMember.Kvid;
relation.AmountInvoice = amount;
relation.AmountUsed = amount;
apply.AmountUsed = applyRelations.Sum(o => o.Amount) + amount;//更新apply的值
apply.AddOnlyProperties(o => o.AmountUsed);
var payments = new List<Payment>();
payments.Add(payment);
var correlatedInvoices = existInvoices.Correlating(payments);//生成payment和invoice之间的关联,同事原Invoices也要更新相关数据
#region 优化
if (!splitPayments.IsNullOrEmpty())//要是所选Payment存在拆分项,满足条件就要更新AmountInvoice
{
decimal amountInvoice = 0;
amountInvoice.PopulateWith(payment.AmountInvoice);// = payment.AmountInvoice;
foreach (var item in splitPayments)
{
if (amountInvoice <= 0) continue;
item.AmountInvoice = item.Amount >= amountInvoice ? amountInvoice : item.Amount;
item.AddOnlyProperties(o => o.AmountInvoice);
amountInvoice -= item.AmountInvoice;
}
}
#endregion
var trans = conn.OpenTransaction();
try
{
conn.Insert(relation);
conn.UpdateOnly(apply);
conn.UpdateOnly(payment);
if (!splitPayments.IsNullOrEmpty())
{
foreach (var item in splitPayments)
{
if (item.OnlyProperties.IsNullOrEmpty()) continue;
conn.UpdateOnly(item);
}
}
foreach (var item in correlatedInvoices)
{
item.SerialNumber = item.GetSubSerialNumber(conn);
conn.Insert(item);
}
foreach (var item in existInvoices)
{
if (item.OnlyProperties.Count > 0)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "冲销撤销")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceDebitCancel : RestfulExecution<Invoice>
{
public Guid ApplyKvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (ApplyKvid == Guid.Empty) throw new Exception("请传入借票申请对象!");
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var apply = conn.SingleById<InvoiceApply>(ApplyKvid);
apply.ThrowIfNull("未找到所选发票申请信息!");
(apply.OperateType != InvoiceApplyType.Debit).ThrowIfTrue("非借票不可撤销!");
(apply.OffsetKvid != Guid.Empty).ThrowIfTrue("当前借票申请已冲账,无法继续操作!");
(apply.Status != (int)InvoiceApplyStatus.Completed).ThrowIfTrue("当前借票申请未完成开票,无法继续操作!");
var allInvoices = conn.Select<Invoice>(o => o.ApplyKvid == apply.Kvid && o.OffsetKvid == Guid.Empty);
if (allInvoices.IsNullOrEmpty()) throw new Exception("未找到所选申请对应的发票信息!");
var existInvoices = allInvoices.Where(o => o.RootKvid == o.Kvid).ToList();//发票信息
if (existInvoices.IsNullOrEmpty()) throw new Exception("未找到所选申请对应的发票信息!");
var relationInvoices = allInvoices.Where(o => o.Type == "Relation" && o.OffsetKvid == Guid.Empty).ToList();//关联Payment的发票数据
var applyRelations = conn.Select<InvoiceApply>(o => o.ParentKvid == apply.Kvid && o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);//关联Payment的申请数据
if (applyRelations.IsNullOrEmpty()) throw new Exception("当前申请无需冲销撤销");
var existPayments = conn.Select<Payment>(o => Sql.In(o.Kvid, applyRelations.ConvertAll(p => p.BizKvid)));//Type:Bank,Split,Pos,Cash,WeChat...
if (existPayments.IsNullOrEmpty()) throw new Exception("当前申请未找到到账冲销,无需撤销!");
List<Payment> parentOrNodePayments = new List<Payment>();//父级或子级payment也要更新AmountInvoice
var existPaymentRootKvids = existPayments.Where(o => o.RootKvid != o.Kvid).ToList().ConvertAll(p => p.RootKvid);
if (!existPaymentRootKvids.IsNullOrEmpty())
{
var parents = conn.Select<Payment>(o => o.OffsetKvid == Guid.Empty && Sql.In(o.Kvid, existPaymentRootKvids));
if (!parents.IsNullOrEmpty()) parentOrNodePayments.AddRange(parents);
}
var nodes = conn.Select<Payment>(o => Sql.In(o.ParentKvid, existPayments.ConvertAll(p => p.Kvid)) && o.OffsetKvid == Guid.Empty && o.Type == PaymentType.Split);
if (!nodes.IsNullOrEmpty()) parentOrNodePayments.AddRange(nodes);
var rtns = new RestfulUpdateResponse<Invoice>();
rtns.Results = new List<Invoice>();
var trans = conn.OpenTransaction();
try
{
//删除关联payment的申请数据
var updateApplyRelations = conn.From<InvoiceApply>();
updateApplyRelations = updateApplyRelations.Update(o => o.Status);
updateApplyRelations = updateApplyRelations.Where(o => Sql.In(o.Kvid, applyRelations.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<InvoiceApply>(new InvoiceApply { Status = -1 }, updateApplyRelations);
//删除关联Payment的发票数据
if (!relationInvoices.IsNullOrEmpty())
{
var updateRelationInvoices = conn.From<Invoice>();
updateRelationInvoices = updateRelationInvoices.Update(o => o.Status);
updateRelationInvoices = updateRelationInvoices.Where(o => Sql.In(o.Kvid, relationInvoices.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<Invoice>(new Invoice { Status = -1 }, updateRelationInvoices);
}
//更新申请的到账金额
apply.AmountUsed = 0;
apply.AddOnlyProperties(o => o.AmountUsed);
conn.UpdateOnly(apply);
//更新发票的付款时间和到账金额
var updateExistInvoices = conn.From<Invoice>();
updateExistInvoices = updateExistInvoices.Update(o => new { o.PayedTime, o.AmountPayment });
updateExistInvoices = updateExistInvoices.Where(o => Sql.In(o.Kvid, existInvoices.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<Invoice>(new Invoice { PayedTime = null, AmountPayment = 0 }, updateExistInvoices);
//更新已关联的payment的AmountInvoice
var updateExistPayments = conn.From<Payment>();
updateExistPayments = updateExistPayments.Update(o => o.AmountInvoice);
updateExistPayments = updateExistPayments.Where(o => Sql.In(o.Kvid, existPayments.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<Payment>(new Payment { AmountInvoice = 0 }, updateExistPayments);
if (!parentOrNodePayments.IsNullOrEmpty())
{
//更新已关联的payment的父级或子级AmountInvoice
var updateParentOrNodePayments = conn.From<Payment>();
updateParentOrNodePayments = updateParentOrNodePayments.Update(o => o.AmountInvoice);
updateParentOrNodePayments = updateParentOrNodePayments.Where(o => Sql.In(o.Kvid, parentOrNodePayments.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<Payment>(new Payment { AmountInvoice = 0 }, updateParentOrNodePayments);
}
rtns.Results.AddRange(existInvoices);
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
#region 申请开票相关
[Api(Description = "开票申请")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyApply : RestfulExecution<InvoiceApply>
{
public InvoiceApply Item { get; set; }
public List<Guid> PaymentKvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
Item.ThrowIfNull("请传入要申请的内容!");
if (Item.PayerName.IsNullOrEmpty()) throw new Exception("请输入发票抬头!");
if (Item.Currency == CurrencyUnit.Unsupported) throw new Exception("不支持的货币单位!");
if (Item.Amount <= 0) throw new Exception("申请金额不能小于0!");
if (Item.OperateType != InvoiceApplyType.Debit) PaymentKvids.ThrowIfNullOrEmpty("请先选择相应收款!");
//申请人为当前登录人
Item.OperatorName = KiviiContext.CurrentMember.FullName;
Item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
//申请所属部门
Item.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
Item.OwnerName = KiviiContext.CurrentMember.DepartmentName;
var rtns = new RestfulQueryResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
if (Item.OperateType == InvoiceApplyType.Debit)
{
var applyKvid = Guid.NewGuid();
Item.RootKvid = applyKvid;
Item.ParentKvid = Guid.Empty;
Item.AmountUsed = 0;
Item.OperateTime = DateTime.Now;
Item.Status = (int)InvoiceApplyStatus.FinancialApproval;
Item.Kvid = applyKvid;
conn.Insert(Item);
rtns.Results.Add(Item);
}
else
{
var paymentKvidsDistinct = PaymentKvids.Distinct().ToList();
var payments = conn.Select<Payment>(o => Sql.In(o.Kvid, paymentKvidsDistinct));
(payments.Count <= 0 || payments.Count != paymentKvidsDistinct.Count).ThrowIfTrue("收款信息与所选项目不一致!");
(payments.Exists(o => o.Type != PaymentType.Bank && o.Type != PaymentType.Split && o.Type != PaymentType.AliPay && o.Type != PaymentType.Pos && o.Type != PaymentType.WeChat && o.Type != PaymentType.Cash)).ThrowIfTrue("请选择正确的收款信息进行申请!");
if (Item.Amount != payments.Sum(o => o.Amount - o.AmountInvoice)) throw new Exception("申请金额与所选到账金额不一致!");
(conn.Exists<InvoiceApply>(o => o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty && Sql.In(o.BizKvid, payments.ConvertAll(p => p.Kvid)))).ThrowIfTrue("已有申请开票中的到账!");
(conn.Exists<Invoice>(o => o.OffsetKvid != Guid.Empty && Sql.In(o.BizKvid, payments.ConvertAll(p => p.RootKvid)))).ThrowIfTrue("已有开票的到账!");
var trans = conn.OpenTransaction();
try
{
var applyKvid = Guid.NewGuid();
Item.RootKvid = applyKvid;
Item.ParentKvid = Guid.Empty;
Item.AmountUsed = Item.Amount;
Item.OperateTime = DateTime.Now;
Item.Status = (int)InvoiceApplyStatus.FinancialApproval;
Item.Kvid = applyKvid;
conn.Insert(Item);
rtns.Results.Add(Item);
foreach (var item in payments)
{
var relation = new InvoiceApply();
relation.PopulateWith(Item);
relation.Kvid = Guid.NewGuid();
relation.RootKvid = applyKvid;
relation.ParentKvid = applyKvid;
relation.SerialNumber = string.Empty;
relation.OperateType = InvoiceApplyType.Related;
relation.BizId = item.SerialNumber;
relation.BizKvid = item.Kvid;
relation.BizType = typeof(Payment).FullName;
relation.Type = Item.OperateType.ToString();
relation.Amount = item.Amount - item.AmountInvoice;
relation.OperatorName = KiviiContext.CurrentMember.FullName;
relation.OperatorKvid = KiviiContext.CurrentMember.Kvid;
relation.AmountInvoice = 0;
relation.AmountUsed = relation.Amount;
conn.Insert(relation);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
}
rtns.Total = rtns.Results.Count();
return rtns;
}
}
[Api(Description = "批量借票申请")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyMultiApply : RestfulExecution<InvoiceApply>
{
public InvoiceApply Item { get; set; }
public List<InvoiceApply> Items { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Item == null && Items.IsNullOrEmpty()).ThrowIfTrue("请传入要借票信息!");
if (Items.IsNullOrEmpty()) Items = new List<InvoiceApply>();
if (Item != null) Items.Add(Item);
if (Items.Exists(o => o.PayerName.IsNullOrEmpty())) throw new Exception("存在发票抬头为空!");
if (Items.Exists(o => o.PayerTaxNumber.IsNullOrEmpty())) throw new Exception("存在税号为空!");
if (Items.Exists(o => o.Currency == CurrencyUnit.Unsupported)) throw new Exception("存在不支持的货币单位!");
if (Items.Exists(o => o.Amount <= 0)) throw new Exception("申请金额不能小于0!");
if (Items.Exists(o => o.OperateType != InvoiceApplyType.Debit)) throw new Exception("此接口仅针对借票申请开票!");
var rtns = new RestfulQueryResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
foreach (var item in Items)
{
#region 创建借票申请
var applyKvid = Guid.NewGuid();
item.OperateType = InvoiceApplyType.Debit;
//申请人为当前登录人
item.OperatorName = KiviiContext.CurrentMember.FullName;
item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
//申请所属部门
item.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
item.OwnerName = KiviiContext.CurrentMember.DepartmentName;
item.RootKvid = applyKvid;
item.ParentKvid = Guid.Empty;
item.AmountUsed = 0;
item.OperateTime = DateTime.Now;
item.Status = (int)InvoiceApplyStatus.FinancialApproval;
item.Kvid = applyKvid;
conn.Insert(item);
#endregion
rtns.Results.Add(item);
}
rtns.Total = rtns.Results.Count();
return rtns;
}
}
#endregion
#region 申请处理相关
[Api(Description = "申请驳回")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyReject : RestfulExecution<InvoiceApply>
{
public List<Guid> ApplyKvids { get; set; }
public string Summary { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var applys = conn.Select<InvoiceApply>(o => Sql.In(o.Kvid, ApplyKvids) && o.OperateType != InvoiceApplyType.Related);
if (applys == null) throw new Exception("无相关申请!");
var applyRelations = conn.Select<InvoiceApply>(o => Sql.In(o.ParentKvid, applys.ConvertAll(p => p.Kvid)) && o.OperateType == InvoiceApplyType.Related);
var rtns = new RestfulExecutionResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var trans = conn.OpenTransaction();
try
{
foreach (var apply in applys)
{
#region apply驳回
//进入开票状态后不可驳回
if (apply.Status >= (int)InvoiceApplyStatus.FinancialExecute) continue;
//if (apply.Status < (int)ApplyStatus.ProcessAdoption) continue;
apply.Status = (int)InvoiceApplyStatus.ApplyReject;
apply.AddOnlyProperties(o => o.Status);
apply.AmountUsed = 0;
apply.AddOnlyProperties(o => o.AmountUsed);
apply.Summary += " (驳回原因:" + Summary + ")";
apply.AddOnlyProperties(o => o.Summary);
conn.UpdateOnly(apply);
apply.RemoveAllOnlyProperties();
rtns.Results.Add(apply);
if (applyRelations.IsNullOrEmpty()) continue;
var relations = applyRelations.Where(o => o.ParentKvid == apply.Kvid).ToList();
if (relations.IsNullOrEmpty()) continue;
#endregion
#region 关联apply 删除
foreach (var item in relations)
{
item.Status = -1;
item.AddOnlyProperties(o => o.Status);
conn.UpdateOnly(item);
}
#endregion
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "执行开票")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyExecute : RestfulExecution<InvoiceApply>
{
public InvoiceApply Item { get; set; }
public InvoiceApplyDetail Detail { get; set; }
public List<InvoiceApplyDetail> Details { get; set; }
//发票开具最大额度限制 默认10万
public decimal Limit { get; set; } = 100000;
public override object OnExecution(IRequest req, IResponse res)
{
(Detail == null && Details.IsNullOrEmpty()).ThrowIfTrue("Need Detail Or Details!");
if (Details.IsNullOrEmpty()) Details = new List<InvoiceApplyDetail>();
if (Detail != null) Details.Add(Detail);
if (Item.Kvid == Guid.Empty) throw new Exception("请先选择申请条目!");
if (Details.Exists(o => o.GoodsFullName.IsNullOrEmpty())) throw new Exception("明细名称存在空值!");
if (Details.Exists(o => o.TaxRate < 0) || Details.Exists(o => o.TaxRate > 1)) throw new Exception("存在明细税率设置范围不在0-1之间!");
if (Details.Exists(o => o.Quantity <= 0)) throw new Exception("请填写数量!");
if (Details.Count > 1)
{
var group = Details.GroupBy(o => o.GoodsId);
foreach (var kv in group)
{
var sum = kv.Sum(o => o.Amount);
if (sum > Limit) throw new Exception($"组别:{kv.Key},总额:{sum}元,金额总和不能超过限额{Limit}元!");
}
}
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var apply = conn.SingleById<InvoiceApply>(Item.Kvid);
if (apply == null) throw new Exception("不存在的申请!");
(apply.OperateType == InvoiceApplyType.Related).ThrowIfTrue("申请类型不正确!");
if (apply.Status >= (int)InvoiceApplyStatus.FinancialExecute) throw new Exception("请勿重复申请开票!");
if (apply.Status < (int)InvoiceApplyStatus.ProcessAdoption) throw new Exception("此申请还在审批中...");
if (apply.Amount != Details.Sum(o => o.Amount)) throw new Exception("总金额和明细总额不一致!");
var existDetails = conn.Select<InvoiceApplyDetail>(o => o.ApplyKvid == apply.Kvid);
var applyRelations = conn.Select<InvoiceApply>(o => o.ParentKvid == apply.Kvid && o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);
var payments = conn.Select<Payment>(o => Sql.In(o.Kvid, applyRelations.ConvertAll(p => p.BizKvid)));//Type:Split,Pos,Cash,WeChat...
(conn.Exists<Invoice>(o => o.OffsetKvid != Guid.Empty && Sql.In(o.BizKvid, payments.ConvertAll(p => p.RootKvid)))).ThrowIfTrue("已有开票的到账,无法重复开票,请驳回!");
#region 计算(不根据前端传来)
foreach (var detail in Details)
{
detail.AmountUntaxed = Math.Round(detail.Amount / (1 + detail.TaxRate), 2);
detail.AmountTax = detail.Amount - detail.AmountUntaxed;
detail.QuantityUnitPriceUntaxed = Math.Round(detail.AmountUntaxed / detail.Quantity, 2);
detail.QuantityUnitPrice = Math.Round(detail.Amount / detail.Quantity, 2);
}
#endregion
var rtns = new RestfulExecutionResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var trans = conn.OpenTransaction();
try
{
#region 申请状态进入开票状态
apply.Status = (int)InvoiceApplyStatus.FinancialExecute;
apply.AddOnlyProperties(o => o.Status);
apply.Summary = Item.Summary;
apply.AddOnlyProperties(o => o.Summary);
apply.Remark = Item.Remark;
apply.AddOnlyProperties(o => o.Remark);
apply.Metadata["PayeeOperatorName"] = Item.Metadata.ContainsKey("PayeeOperatorName") ? Item.Metadata["PayeeOperatorName"] : KiviiContext.CurrentMember.FullName;
apply.Metadata["ReviewerName"] = Item.Metadata.ContainsKey("ReviewerName") ? Item.Metadata["ReviewerName"] : KiviiContext.CurrentMember.FullName;
apply.AddOnlyProperties(o => o.Metadata);
conn.UpdateOnly(apply);
rtns.Results.Add(apply);
#endregion
#region 申请明细入库(发票明细)
foreach (var detail in Details)
{
detail.ApplyKvid = apply.Kvid;
conn.Insert(detail);
}
#endregion
if (!existDetails.IsNullOrEmpty())
{
foreach (var exist in existDetails)
{
exist.Status = -1;
exist.AddOnlyProperties(o => o.Status);
conn.UpdateOnly(exist);
}
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "申请合并")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyCombine : RestfulExecution<InvoiceApply>
{
public InvoiceApply Item { get; set; }
public List<Guid> Kvids { get; set; }
public InvoiceApplyDetail Detail { get; set; }
public List<InvoiceApplyDetail> Details { get; set; }
//发票开具最大额度限制 默认10万
public decimal Limit { get; set; } = 100000;
public override object OnExecution(IRequest req, IResponse res)
{
(Detail == null && Details.IsNullOrEmpty()).ThrowIfTrue("Need Detail Or Details!");
if (Details.IsNullOrEmpty()) Details = new List<InvoiceApplyDetail>();
if (Detail != null) Details.Add(Detail);
if (Kvids == null || Kvids.Count <= 0) throw new Exception("请先选择申请条目!");
if (Item.OperateType != InvoiceApplyType.Payment && Item.OperateType != InvoiceApplyType.Pos && Item.OperateType != InvoiceApplyType.Cash && Item.OperateType != InvoiceApplyType.AliPay && Item.OperateType != InvoiceApplyType.WeChat) throw new Exception("合并开票只针对收款刷卡现金申请!");
if (Details.Exists(o => o.GoodsFullName.IsNullOrEmpty())) throw new Exception("明细名称存在空值!");
if (Details.Exists(o => o.TaxRate < 0) || Details.Exists(o => o.TaxRate > 1)) throw new Exception("存在明细税率设置范围不在0-1之间!");
if (Details.Exists(o => o.Quantity <= 0)) throw new Exception("请填写数量!");
var group = Details.GroupBy(o => o.GoodsId);
foreach (var kv in group)
{
var sum = kv.Sum(o => o.Amount);
if (sum > Limit) throw new Exception($"组别:{kv.Key},总额:{sum}元,金额总和不能超过限额{Limit}元!");
//if (sum > Limit) throw new Exception($"组别:{kv.Key},总额:{sum}元,金额总和不能超过10万!");
}
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var applys = conn.Select<InvoiceApply>(o => Sql.In(o.Kvid, Kvids));
if (applys.Count <= 0 || applys == null) throw new Exception("不存在的申请!");
if (applys.Count < 2) throw new Exception("请选择两条以上的申请进行合并!");
//if (applys.Exists(o => o.Type != "VATS")) throw new Exception("普票暂时无法合并开票");
if (applys.Exists(o => o.PayerName != Item.PayerName)) throw new Exception("合并申请抬头不一致!");
if (!applys.Exists(o => Sql.In(o.OperateType, InvoiceApplyType.Payment, InvoiceApplyType.Pos, InvoiceApplyType.Cash, InvoiceApplyType.AliPay, InvoiceApplyType.WeChat))) throw new Exception("合并开票只针对收款刷卡现金申请!");
if (applys.Exists(o => o.OperateType == InvoiceApplyType.Payment)) Item.OperateType = InvoiceApplyType.Payment;
var groups = applys.GroupBy(o => new { o.PayerName, o.PayerTaxNumber });
if (groups.Count() != 1) throw new Exception("合并申请抬头不一致!");
if (applys.Exists(o => o.Status >= (int)InvoiceApplyStatus.FinancialExecute)) throw new Exception("请勿重复申请开票!");
if (applys.Exists(o => o.Status < (int)InvoiceApplyStatus.ProcessAdoption)) throw new Exception("此申请还在审批中...");
if (applys.Sum(o => o.Amount) != Item.Amount) throw new Exception("合并金额与申请金额不一致!");
if (Item.Amount != Details.Sum(o => o.Amount)) throw new Exception("总金额和明细总额不一致!");
var remark = $"[总: {Item.Amount}元]";
foreach (var item in applys)
{
remark += $"[{item.OwnerName}: {item.Amount}元]";
}
#region 计算(不根据前端传来)
foreach (var detail in Details)
{
detail.AmountUntaxed = Math.Round(detail.Amount / (1 + detail.TaxRate), 2);
detail.AmountTax = detail.Amount - detail.AmountUntaxed;
detail.QuantityUnitPriceUntaxed = Math.Round(detail.AmountUntaxed / detail.Quantity, 2);
detail.QuantityUnitPrice = Math.Round(detail.Amount / detail.Quantity, 2);
}
#endregion
var rtns = new RestfulExecutionResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var trans = conn.OpenTransaction();
try
{
var newInvoiceApplyKvid = Guid.NewGuid();
#region 创建新的Apply
var newApply = new InvoiceApply();
newApply.PopulateWith(Item);
newApply.SerialNumber = string.Empty;
newApply.RootKvid = newInvoiceApplyKvid;
newApply.ParentKvid = Guid.Empty;
newApply.Kvid = newInvoiceApplyKvid;
newApply.Amount = applys.Sum(o => o.Amount);
newApply.AmountUsed = applys.Sum(o => o.AmountUsed);
newApply.Status = (int)InvoiceApplyStatus.FinancialApproval;
newApply.Summary = Item.Summary;
newApply.Remark = remark;
newApply.Metadata["PayeeOperatorName"] = Item.Metadata.ContainsKey("PayeeOperatorName") ? Item.Metadata["PayeeOperatorName"] : KiviiContext.CurrentMember.FullName;
newApply.Metadata["ReviewerName"] = Item.Metadata.ContainsKey("ReviewerName") ? Item.Metadata["ReviewerName"] : KiviiContext.CurrentMember.FullName;
conn.Insert(newApply);
rtns.Results.Add(newApply);
#endregion
#region ApplyDetail入库
foreach (var detail in Details)
{
detail.ApplyKvid = newInvoiceApplyKvid;
conn.Insert(detail);
}
#endregion
#region Apply更新(更改状态和biz记录新生产的Apply)
foreach (var item in applys)
{
item.AmountUsed = 0;
item.AddOnlyProperties(o => o.AmountUsed);
item.BizType = typeof(InvoiceApply).FullName;
item.AddOnlyProperties(o => o.BizType);
item.BizKvid = newInvoiceApplyKvid;
item.AddOnlyProperties(o => o.BizKvid);
item.Status = (int)InvoiceApplyStatus.ApplyMerge;
item.AddOnlyProperties(o => o.Status);
conn.UpdateOnly(item);
}
#endregion
#region 对应收款关联的ApplyKvid变更
var relationApplys = conn.Select<InvoiceApply>(o => Sql.In(o.ParentKvid, applys.Select(p => p.Kvid)) && o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);
//var relationApplys = conn.Select<InvoiceApply>(o => Sql.In(o.ParentKvid, applys.Select(p => p.Kvid).ToList()) && o.OperateType == InvoiceApplyType.Related);
if (!relationApplys.IsNullOrEmpty())
{
foreach (var relation in relationApplys)
{
relation.ParentKvid = newInvoiceApplyKvid;
relation.AddOnlyProperties(o => o.ParentKvid);
relation.RootKvid = newInvoiceApplyKvid;
relation.AddOnlyProperties(o => o.RootKvid);
conn.UpdateOnly(relation);
}
}
#endregion
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "修改申请类型,专票普票申请之间的改变")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class InvoiceApplyChangeType : RestfulExecution<InvoiceApply>
{
public Guid Kvid { get; set; }
public List<Guid> Kvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Kvid == Guid.Empty && Kvids.IsNullOrEmpty()).ThrowIfTrue("Need Kvid Or Kvids!");
if (Kvids.IsNullOrEmpty()) Kvids = new List<Guid>();
if (Kvid != null) Kvids.Add(Kvid);
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var applys = conn.SelectByIds<InvoiceApply>(Kvids);
applys.ThrowIfNullOrEmpty("未找到对应的申请!");
(applys.Exists(o => o.Status > (int)InvoiceApplyStatus.FinancialExecute)).ThrowIfTrue("申请已执行开票,不可改变");
var rtns = new RestfulUpdateResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
foreach (var item in applys)
{
if (item.Type == "VAT")
{
item.Type = "VATS";
item.AddOnlyProperties(o => o.Type);
conn.UpdateOnly(item);
item.RemoveAllOnlyProperties();
rtns.Results.Add(item);
}
else if (item.Type == "VATS")
{
item.Type = "VAT";
item.AddOnlyProperties(o => o.Type);
conn.UpdateOnly(item);
item.RemoveAllOnlyProperties();
rtns.Results.Add(item);
}
}
return rtns;
}
}
#endregion
#region 开票客户端对接
[RequiresAnyRole(SystemRoles.Everyone)]
[Api(Description = "获取一条")]
public class InvoiceApplyExecuting : RestfulExecution<InvoiceApply>
{
/// <summary>
/// 优先级OperatorKvid>OwnerKvid>OrganizationKvid
/// </summary>
public Guid OperatorKvid { get; set; }
public Guid OwnerKvid { get; set; }
public Guid OrganizationKvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var query = conn.From<InvoiceApply>();
query.Where(o => o.Status == (int)InvoiceApplyStatus.FinancialExecute);
if (OperatorKvid != Guid.Empty) query.And(o => o.OperatorKvid == OperatorKvid);
else if (OwnerKvid != Guid.Empty) query.And(o => o.OwnerKvid == OwnerKvid);
else if (OrganizationKvid != Guid.Empty) query.And(o => o.OrganizationKvid == OrganizationKvid);
var apply = conn.Single(query);
var rtns = new RestfulQueryResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
if (apply == null) return rtns;
apply.Status = (int)InvoiceApplyStatus.AutoExecuting;
apply.AddOnlyProperties(o => o.Status);
conn.UpdateOnly(apply);
apply.RemoveAllOnlyProperties();
rtns.Results.Add(apply);
rtns.Total = rtns.Results.Count;
return rtns;
}
}
[RequiresAnyRole(SystemRoles.Everyone)]
[Api(Description = "开票结果回写")]
public class InvoiceApplyResult : RestfulExecution<InvoiceApply>
{
public Guid ApplyKvid { get; set; }
public string FileContent { get; set; }
//发票开具最大额度限制 默认10万
public decimal Limit { get; set; } = 100000;
public override object OnExecution(IRequest req, IResponse res)
{
ApplyKvid.ThrowIfEmpty("发票申请Kvid 不能为空!");
FileContent.ThrowIfNullOrEmpty("开票结果内容不能为空!");
var contents = FileContent.Replace("\r", "\n").Split('\n').ToList();
List<List<string>> infos = new List<List<string>>();
foreach (var content in contents)
{
if (content.IsNullOrEmpty()) continue;
var info = content.Replace(" ", "").Replace("[", "").Replace("]", ",").Replace("\n", "").Split(',').ToList();
(info.Count() < 3).ThrowIfTrue("文件内容格式不正确!");
infos.Add(info);
}
var conn = KiviiContext.GetOpenedDbConnection<InvoiceApply>();
var apply = conn.SingleById<InvoiceApply>(ApplyKvid);
apply.ThrowIfNull("未找到对应发票申请信息!");
(apply.AmountInvoice == apply.Amount).ThrowIfTrue("此发票申请已开具发票!");
var applyDetails = conn.Select<InvoiceApplyDetail>(o => o.ApplyKvid == ApplyKvid);
applyDetails.ThrowIfNullOrEmpty("未找到对应发票申请明细信息!");
//拆分applyDetail (超10万的)
var applyDetailSplited = new List<InvoiceApplyDetail>();
//只有一条明细的情况下 需要对明细进行拆分超过10万的部分
if (applyDetails.Count == 1) applyDetailSplited = applyDetails[0].SplitApplyDetail(Limit);
//多条明细的情况 不进行拆分了 但要判断下要是超过10万就要报错了
else
{
var group = applyDetails.GroupBy(o => o.GoodsId);
foreach (var kv in group)
{
var sum = kv.Sum(o => o.Amount);
if (sum > Limit) throw new Exception($"存在单张发票明细超过{Limit}元 无法创建!");
}
applyDetailSplited = applyDetails;
}
(applyDetailSplited.Sum(o => o.Amount) != apply.Amount).ThrowIfTrue("要生成的发票金额之和不等于申请金额!");
var applyRelations = conn.Select<InvoiceApply>(o => o.ParentKvid == apply.Kvid && o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);
var logs = new List<EntityLog<InvoiceApply>>();
var updateApplys = new List<InvoiceApply>();
var insertInvoices = new List<Invoice>();
var insertInvoiceDetails = new List<InvoiceDetail>();
foreach (var info in infos)
{
#region 开票失败的情况处理
if (info[2] != "开具结果:1")
{
apply.Status = (int)InvoiceApplyStatus.ErrorInsertInvoice;
apply.AddOnlyProperties(o => o.Status);
apply.Remark = $"开票失败! {info.Join(",")}";
apply.AddOnlyProperties(o => o.Remark);
apply.AmountUsed = 0;
apply.AddOnlyProperties(o => o.AmountUsed);
updateApplys.Add(apply);
//conn.UpdateOnly(apply);
if (!applyRelations.IsNullOrEmpty())
{
foreach (var item in applyRelations)
{
item.Status = -1;
item.AddOnlyProperties(o => o.Status);
updateApplys.Add(item);
//conn.UpdateOnly(item);
}
}
var log = new EntityLog<InvoiceApply>();
log.OwnerKvid = apply.Kvid;
log.BizId = apply.SerialNumber;
log.BizKvid = apply.Kvid;
log.BizType = typeof(InvoiceApply).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = $"编号:{apply.SerialNumber} 申请失败,原因:{info.Join(",")}";
logs.Add(log);
continue;
}
#endregion
#region 开票成功的情况处理
var time = DateTime.ParseExact(info[0], "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);//开票时间
var code = info[4];//开票代码
var number = System.Text.RegularExpressions.Regex.Replace(info[5], @"[^0-9]+", "");//开票号码,唯一
var type = info[3].Split(':')[1];//开票类型
var group = info[1].Split(':')[1];
var currentDetails = group.IsNullOrEmpty() ? applyDetailSplited : applyDetailSplited.Where(o => o.GoodsId == group).ToList();
var invoiceKvid = Guid.NewGuid();
var invoice = new Invoice();
invoice.RootKvid = invoiceKvid;
invoice.Kvid = invoiceKvid;
invoice.ApplyKvid = ApplyKvid;
invoice.OwnerKvid = apply.OwnerKvid;
invoice.OwnerName = apply.OwnerName;
invoice.Currency = CurrencyUnit.CNY;
invoice.Type = type;
invoice.Category = apply.OperateType.ToString();
invoice.OperateTime = time;
invoice.SerialCode = code;
invoice.SerialNumber = number;
invoice.Status = 1;
invoice.Amount = currentDetails.Sum(o => o.Amount);
invoice.AmountTax = currentDetails.Sum(o => o.AmountTax);
invoice.AmountUntaxed = currentDetails.Sum(o => o.AmountUntaxed);
invoice.TaxRate = currentDetails[0].TaxRate;
invoice.PayeeKvid = apply.PayeeKvid;
invoice.PayeeName = apply.PayeeName;
invoice.PayeeTaxNumber = apply.PayeeTaxNumber;
invoice.PayeeCompanyAddress = apply.PayeeCompanyAddress;
invoice.PayeePhone = apply.PayeePhone;
invoice.PayeeRegisteredBank = apply.PayeeRegisteredBank;
invoice.PayeeBankAccount = apply.PayeeBankAccount;
invoice.PayerKvid = apply.PayerKvid;
invoice.PayerName = apply.PayerName;
invoice.PayerTaxNumber = apply.PayerTaxNumber;
invoice.PayerCompanyAddress = apply.PayerCompanyAddress;
invoice.PayerPhone = apply.PayerPhone;
invoice.PayerRegisteredBank = apply.PayerRegisteredBank;
invoice.PayerBankAccount = apply.PayerBankAccount;
invoice.OperatorName = apply.OperatorName;
invoice.Remark = apply.Remark;
invoice.Metadata = new Dictionary<string, string>();
invoice.Metadata["PayeeOperatorName"] = apply.Metadata.ContainsKey("PayeeOperatorName") ? apply.Metadata["PayeeOperatorName"] : "";
invoice.Metadata["ReviewerName"] = apply.Metadata.ContainsKey("ReviewerName") ? apply.Metadata["ReviewerName"] : "";
insertInvoices.Add(invoice);
//conn.Insert(invoice);
foreach (var item in currentDetails)
{
var detail = new InvoiceDetail();
detail.InvoiceKvid = invoiceKvid;
detail.GoodsFullName = item.GoodsFullName;
detail.GoodsSpecifications = item.GoodsSpecifications;
detail.Quantity = item.Quantity;
detail.QuantityUnitPriceUntaxed = item.QuantityUnitPriceUntaxed;
detail.AmountUntaxed = item.AmountUntaxed;
detail.Amount = item.Amount;
detail.AmountTax = item.AmountTax;
detail.TaxRate = item.TaxRate;
insertInvoiceDetails.Add(detail);
//conn.Insert(detail);
}
#endregion
#region 更新申请状态
if (!applyRelations.IsNullOrEmpty())
{
foreach (var item in applyRelations)
{
//无需更新
if (item.AmountInvoice >= item.Amount) continue;
var relationAmount = item.Amount - item.AmountInvoice;
var amount = relationAmount <= invoice.Amount ? relationAmount : invoice.Amount;
item.AmountInvoice += amount;
item.AddOnlyProperties(o => o.AmountInvoice);
if (item.AmountInvoice == item.Amount)
{
item.Status = (int)InvoiceApplyStatus.Completed;
item.AddOnlyProperties(o => o.Status);
}
updateApplys.Add(item);
//conn.UpdateOnly(item);
}
}
apply.AmountInvoice += invoice.Amount;
apply.AddOnlyProperties(o => o.AmountInvoice);
if (apply.AmountInvoice == apply.Amount)
{
apply.Status = (int)InvoiceApplyStatus.Completed;
apply.AddOnlyProperties(o => o.Status);
}
updateApplys.Add(apply);
//conn.UpdateOnly(apply);
#endregion
}
#region 收款与发票关联
var payments = conn.Select<Payment>(o => Sql.In(o.Kvid, applyRelations.ConvertAll(p => p.BizKvid)));//Type:Bank,Split,Pos,Cash,WeChat...
List<Payment> parentPayments = new List<Payment>();//也要更新父级的AmountInvoice
if (!payments.IsNullOrEmpty())
{
var correlatedInvoices = insertInvoices.Correlating(payments);
insertInvoices.AddRange(correlatedInvoices);
var currentRootKvids = payments.Where(o => o.RootKvid != o.Kvid).ToList().ConvertAll(p => p.RootKvid);
if (!currentRootKvids.IsNullOrEmpty()) parentPayments = conn.Select<Payment>(o => o.OffsetKvid == Guid.Empty && Sql.In(o.Kvid, currentRootKvids));
}
#endregion
var rtns = new RestfulUpdateResponse<InvoiceApply>();
rtns.Results = new List<InvoiceApply>();
var trans = conn.OpenTransaction();
try
{
if (!logs.IsNullOrEmpty()) logs.ForEach(o => conn.Insert(o));
if (!updateApplys.IsNullOrEmpty())
{
updateApplys.ForEach(o => conn.UpdateOnly(o));
rtns.Results.AddRange(updateApplys);
}
if (!insertInvoices.IsNullOrEmpty())
{
foreach (var item in insertInvoices)
{
if (item.Type == "Relation") item.SerialNumber = item.GetSubSerialNumber(conn);
conn.Insert(item);
}
}
if (!insertInvoiceDetails.IsNullOrEmpty()) insertInvoiceDetails.ForEach(o => conn.Insert(o));
if (!payments.IsNullOrEmpty())
{
payments.ForEach(o => conn.UpdateOnly(o));
}
if (!parentPayments.IsNullOrEmpty())
{
parentPayments.ForEach(o => o.RecalculateAmountInvoice(conn));
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
#endregion
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "付款登记")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PayPaying : RestfulExecution<Pay>
{
public List<Guid> SettlementKvids { get; set; }
public List<PayingMethod> PayingMethods { get; set; }//付款方式,支持组合付款
public Pay Item { get; set; }
private static MethodInfo _realMethod = null;
public override object OnExecution(IRequest req, IResponse res)
{
SettlementKvids.ThrowIfNullOrEmpty("未传入结算信息,无法付款登记!");
Item.ThrowIfNull("未传入付款登记信息!");
Item.PayerName.ThrowIfNullOrEmpty("未传入付款单位!");
Item.PayeeName.ThrowIfNullOrEmpty("未传入收款单位!");
Item.Currency.ThrowIfNull("货币单位不能为空!");
PayingMethods.ThrowIfNullOrEmpty("未传入付款登记方式!");
PayingMethods.Exists(o => o.Type == PayType.Unsupported).ThrowIfTrue("存在不支持的付款登记方式!");
PayingMethods.Exists(o => o.Kvid == Guid.Empty).ThrowIfTrue("付款登记Kvid不能为空!");
(PayingMethods.Count(o => o.Type == PayType.Discount) > 1).ThrowIfTrue("金额抵扣登记方式超过限制次数,仅可使用一次");
var amountMethod = PayingMethods.Sum(o => o.Amount);
var conn = KiviiContext.GetOpenedDbConnection<Pay>();
var settlements = conn.Select<Settlement>(o => Sql.In(o.Kvid, SettlementKvids));
settlements.ThrowIfNullOrEmpty("未找到结算单!");
if (settlements.Count != SettlementKvids.Count) throw new Exception("所选结算项与实际结算项不符合!");
if (settlements.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("所选结算项存在已作废项!");
if (settlements.Exists(o => o.PayKvid != Guid.Empty)) throw new Exception("所选结算项存在已付款登记!");
var amountSettlement = settlements.Sum(o => o.Amount);
var amountSettlementPlan = settlements.Sum(o => o.AmountPlan);
(amountMethod != amountSettlement).ThrowIfTrue("所选结算总金额与付款总金额不等!");
var rtns = new RestfulExecutionResponse<Pay>();
rtns.Results = new List<Pay>();
var payKvid = Guid.NewGuid();
#region 创建支付单
Item.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
Item.OwnerName = KiviiContext.CurrentMember.DepartmentName;
Item.OperateTime = DateTime.Now;
Item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
Item.OperatorName = KiviiContext.CurrentMember.FullName;
Item.Kvid = payKvid;
Item.SerialNumber = string.Empty;
Item.AmountSettlement = amountSettlement;
Item.Amount = 0;
Item.AmountDiscount = 0;
Item.AmountPayment = 0;
#endregion
foreach (var item in settlements)//更新PayKvid
{
item.PayKvid = payKvid;
item.AddOnlyProperties(o => o.PayKvid);
}
var accountBiz = Item.Currency.GetBizAccount();//conn.Single<Account>(o => o.OwnerKvid == KiviiContext.CurrentMember.OrganizationKvid && o.Type == AccountType.Biz && o.Currency == Item.Currency);
accountBiz.ThrowIfNull($"未能正确获取系统{Item.Currency}业务账户!请联系管理员!");
var accountBalance = accountBiz.Currency.GetBalanceAccount();
accountBalance.ThrowIfNull($"未能正确获取系统{accountBiz.Currency}平衡账户!请联系管理员!");
if (PayingMethods.Count == 1)//单笔付款登记
{
var payingMethod = PayingMethods[0];
if (payingMethod.Type == PayType.Payment)
{
var payment = conn.SingleById<Payment>(payingMethod.Kvid);
payment.ThrowIfNull("所选付款数据不存在!");
(payment.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
if (payment.Type != PaymentType.Split && payment.Type != PaymentType.Pos && payment.Type != PaymentType.Cash && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat) throw new Exception("请选择正确的付款方式!");
if (payment.Amount - payment.AmountUsed < amountSettlement) throw new Exception($"所选付款方式余额不足,可用余额:{payment.Amount - payment.AmountUsed}");
(payment.AmountInvoice != payment.Amount).ThrowIfTrue("所选到账尚未开票,请先开票后再分摊!");
var dynamicSplitPayment = Item.Paying(payment, accountBalance, accountBiz);//insert
var accountDetails = dynamicSplitPayment.Using(accountBalance, accountBiz);//insert
var payments = new List<Payment>();
payments.Add(dynamicSplitPayment);
var payPayments = payments.Correlating(settlements);//建立Payment和Settlement关系,更新Settlement的AmountPayment值
var trans = conn.OpenTransaction();
List<IDbTransaction> subTrans = new List<IDbTransaction>();
try
{
conn.Insert(Item);//创建付款单
rtns.Results.Add(Item);
dynamicSplitPayment.SerialNumber = payment.GetSubSerialNumber(conn);
conn.Insert(dynamicSplitPayment);//创建DynamicSplitPayment
accountDetails.ForEach(o => o.Insert(conn));//创建账户明细
payment.RecalculateAmountUsed(conn);//重新统计到账的使用情况
foreach (var item in payPayments)
{
item.SerialNumber = payment.GetSubSerialNumber(conn);
conn.Insert(item);
}
#region 同步更新泛型Settlement表中数据
foreach (var item in settlements)
{
conn.UpdateOnly(item);//更新了Paykvid和AmountPayment
}
var groupSettlement = settlements.GroupBy(o => o.BizType);
if (_realMethod == null)
{
var function = new Func<IDbConnection, List<Settlement>, object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
foreach (var group in groupSettlement)
{
var entityType = Text.AssemblyUtils.FindType(group.Key);
if (entityType == null) continue;
if (entityType == typeof(Settlement)) continue;
var result = _realMethod.MakeGenericMethod(entityType).Invoke(this, new object[2] { conn, group.ToList() });
if (result != null) subTrans.Add(result as IDbTransaction);
}
#endregion
trans.Commit();
subTrans.ForEach(o => o?.Commit());
}
catch (Exception ex)
{
trans.Rollback();
subTrans.ForEach(o => o?.Rollback());
throw ex;
}
}
if (payingMethod.Type == PayType.Account)
{
var account = conn.SingleById<Account>(payingMethod.Kvid);
account.ThrowIfNull("未找到所选账户!");
(account.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
(account.Type != AccountType.Deposit).ThrowIfTrue("请选择正确的客户存款账户!");
(account.Amount + account.CreditLine < amountSettlement).ThrowIfTrue($"所选付款方式余额不足,可用余额:{account.Amount + account.CreditLine}");
var dynamicSplitPayment = Item.Paying(account, accountBiz);//insert
var accountDetails = dynamicSplitPayment.Using(account, accountBiz);//insert
var payments = new List<Payment>();
payments.Add(dynamicSplitPayment);
var payPayments = payments.Correlating(settlements);//建立Payment和Settlement关系,更新Settlement的AmountPayment值
var trans = conn.OpenTransaction();
List<IDbTransaction> subTrans = new List<IDbTransaction>();
try
{
conn.Insert(Item);//创建付款单
rtns.Results.Add(Item);
conn.Insert(dynamicSplitPayment);//创建DynamicSplitPayment
accountDetails.ForEach(o => o.Insert(conn));//创建账户明细
foreach (var item in payPayments)
{
item.SerialNumber = dynamicSplitPayment.GetSubSerialNumber(conn);
conn.Insert(item);
}
#region 同步更新泛型Settlement表中数据
foreach (var item in settlements)
{
conn.UpdateOnly(item);//更新了Paykvid和AmountPayment
}
var groupSettlement = settlements.GroupBy(o => o.BizType);
if (_realMethod == null)
{
var function = new Func<IDbConnection, List<Settlement>, object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
foreach (var group in groupSettlement)
{
var entityType = Text.AssemblyUtils.FindType(group.Key);
if (entityType == null) continue;
if (entityType == typeof(Settlement)) continue;
var result = _realMethod.MakeGenericMethod(entityType).Invoke(this, new object[2] { conn, group.ToList() });
if (result != null) subTrans.Add(result as IDbTransaction);
}
#endregion
trans.Commit();
subTrans.ForEach(o => o?.Commit());
}
catch (Exception ex)
{
trans.Rollback();
subTrans.ForEach(o => o?.Rollback());
throw ex;
}
}
if (payingMethod.Type == PayType.Discount)
{
var discountAccount = conn.SingleById<Account>(payingMethod.Kvid);
discountAccount.ThrowIfNull("未找到所选账户!");
(discountAccount.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
(discountAccount.Type != AccountType.Discount).ThrowIfTrue("请选择正确的折扣账户!");
(discountAccount.OwnerKvid != KiviiContext.CurrentMember.DepartmentKvid).ThrowIfTrue("存在非本部门的折扣账户!");
var dynamicSplitPayment = Item.Paying(discountAccount, accountBiz);//insert
var discount = Item.Discount(discountAccount, amountSettlement, payingMethod.Remark);
var accountDetails = dynamicSplitPayment.Using(discountAccount, accountBiz);//insert
var payments = new List<Payment>();
payments.Add(dynamicSplitPayment);
var payPayments = payments.Correlating(settlements);//建立Payment和Settlement关系,更新Settlement的AmountPayment值
var trans = conn.OpenTransaction();
List<IDbTransaction> subTrans = new List<IDbTransaction>();
try
{
conn.Insert(Item);//创建付款单
conn.Insert(discount);//创建折扣日志
rtns.Results.Add(Item);
conn.Insert(dynamicSplitPayment);//创建DynamicSplitPayment
accountDetails.ForEach(o => o.Insert(conn));//创建账户明细
foreach (var item in payPayments)
{
item.SerialNumber = dynamicSplitPayment.GetSubSerialNumber(conn);
conn.Insert(item);
}
#region 同步更新泛型Settlement表中数据
foreach (var item in settlements)
{
conn.UpdateOnly(item);//更新了Paykvid和AmountPayment
}
var groupSettlement = settlements.GroupBy(o => o.BizType);
if (_realMethod == null)
{
var function = new Func<IDbConnection, List<Settlement>, object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
foreach (var group in groupSettlement)
{
var entityType = Text.AssemblyUtils.FindType(group.Key);
if (entityType == null) continue;
if (entityType == typeof(Settlement)) continue;
var result = _realMethod.MakeGenericMethod(entityType).Invoke(this, new object[2] { conn, group.ToList() });
if (result != null) subTrans.Add(result as IDbTransaction);
}
#endregion
trans.Commit();
subTrans.ForEach(o => o?.Commit());
}
catch (Exception ex)
{
trans.Rollback();
subTrans.ForEach(o => o?.Rollback());
throw ex;
}
}
return rtns;
}
else//组合付款登记
{
var paymentKvids = PayingMethods.Where(o => o.Type == PayType.Payment).ToList();
var accountKvids = PayingMethods.Where(o => o.Type == PayType.Account).ToList();
var discountKvids = PayingMethods.Where(o => o.Type == PayType.Discount).ToList();
List<Payment> payments = null;
if (!paymentKvids.IsNullOrEmpty())
{
payments = conn.SelectByIds<Payment>(paymentKvids);
payments.ThrowIfNullOrEmpty("未找到所选付款数据!");
payments.Exists(o => o.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
if (payments.Exists(o => o.Type != PaymentType.Split && o.Type != PaymentType.Pos && o.Type != PaymentType.Cash && o.Type != PaymentType.AliPay && o.Type != PaymentType.WeChat)) throw new Exception("请选择正确的付款方式!");
if (payments.Exists(o => (o.Amount - o.AmountUsed) <= 0)) throw new Exception($"所选付款方式余额不足!");
if (payments.Exists(o => (o.AmountInvoice != o.Amount))) throw new Exception($"所选付款方式余额不足!");
}
List<Account> accounts = null;
if (!accountKvids.IsNullOrEmpty())
{
accounts = conn.SelectByIds<Account>(accountKvids);
accounts.ThrowIfNullOrEmpty("未找到所选账户!");
accounts.Exists(o => o.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
if (accounts.Exists(o => o.Type != AccountType.Deposit)) throw new Exception("请选择正确的客户存款账户!");
if (accounts.Exists(o => (o.Amount + o.CreditLine) <= 0)) throw new Exception($"所选付款方式余额不足!");
}
List<Account> discounts = null;
if (!discountKvids.IsNullOrEmpty())
{
discounts = conn.SelectByIds<Account>(discountKvids);
discounts.ThrowIfNullOrEmpty("未找到所选账户!");
discounts.Exists(o => o.Currency != Item.Currency).ThrowIfTrue("存在不相符的货币单位!");
if (discounts.Exists(o => o.Type != AccountType.Discount)) throw new Exception("请选择正确的折扣账户!");
if (discounts.Exists(o => o.OwnerKvid != KiviiContext.CurrentMember.DepartmentKvid)) throw new Exception($"存在非本部门的折扣账户!");
}
var dynamicSplitPayments = new List<Payment>();
var allAccountDetails = new List<AccountDetail>();
var allDiscounts = new List<Discount>();
if (!payments.IsNullOrEmpty())
{
foreach (var item in payments)
{
var payingMethod = PayingMethods.FirstOrDefault(o => o.Kvid == item.Kvid);
if (payingMethod == null) continue;
var dynamicSplitPayment = Item.Paying(item, accountBalance, accountBiz, payingMethod.Amount);//insert
var accountDetails = dynamicSplitPayment.Using(accountBalance, accountBiz);//insert
dynamicSplitPayments.Add(dynamicSplitPayment);
allAccountDetails.AddRange(accountDetails);
}
}
if (!accounts.IsNullOrEmpty())
{
foreach (var item in accounts)
{
var payingMethod = PayingMethods.FirstOrDefault(o => o.Kvid == item.Kvid);
if (payingMethod == null) continue;
var dynamicSplitPayment = Item.Paying(item, accountBiz, payingMethod.Amount);//insert
var accountDetails = dynamicSplitPayment.Using(item, accountBiz);//insert
dynamicSplitPayments.Add(dynamicSplitPayment);
allAccountDetails.AddRange(accountDetails);
}
}
if (!discounts.IsNullOrEmpty())
{
foreach (var item in discounts)
{
var payingMethod = PayingMethods.FirstOrDefault(o => o.Kvid == item.Kvid);
if (payingMethod == null) continue;
var dynamicSplitPayment = Item.Paying(item, accountBiz, payingMethod.Amount);//insert
var discount = Item.Discount(item, amountSettlement, payingMethod.Remark);
var accountDetails = dynamicSplitPayment.Using(item, accountBiz);//insert
allDiscounts.Add(discount);
dynamicSplitPayments.Add(dynamicSplitPayment);
allAccountDetails.AddRange(accountDetails);
}
}
var payPayments = dynamicSplitPayments.Correlating(settlements);//建立Payment和Settlement关系,更新Settlement的AmountPayment值
var trans = conn.OpenTransaction();
List<IDbTransaction> subTrans = new List<IDbTransaction>();
try
{
conn.Insert(Item);//创建付款单
rtns.Results.Add(Item);
foreach (var item in allDiscounts)
{
conn.Insert(item);//创建折扣日志
}
foreach (var item in dynamicSplitPayments)
{
item.SerialNumber = item.GetSubSerialNumber(conn);
conn.Insert(item);//创建DynamicSplitPayment
if (item.RootKvid != item.Kvid) item.RecalculateAmountUsed(conn);//重新统计到账的使用情况
}
allAccountDetails.ForEach(o => o.Insert(conn));//创建账户明细
foreach (var item in payPayments)
{
item.SerialNumber = item.GetSubSerialNumber(conn);
conn.Insert(item);
}
#region 同步更新泛型Settlement表中数据
foreach (var item in settlements)
{
conn.UpdateOnly(item);//更新了Paykvid和AmountPayment
}
var groupSettlement = settlements.GroupBy(o => o.BizType);
if (_realMethod == null)
{
var function = new Func<IDbConnection, List<Settlement>, object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
foreach (var group in groupSettlement)
{
var entityType = Text.AssemblyUtils.FindType(group.Key);
if (entityType == null) continue;
if (entityType == typeof(Settlement)) continue;
var result = _realMethod.MakeGenericMethod(entityType).Invoke(this, new object[2] { conn, group.ToList() });
if (result != null) subTrans.Add(result as IDbTransaction);
}
#endregion
trans.Commit();
subTrans.ForEach(o => o?.Commit());
}
catch (Exception ex)
{
trans.Rollback();
subTrans.ForEach(o => o?.Rollback());
throw ex;
}
return rtns;
}
}
private IDbTransaction onExecution<T>(IDbConnection conn, List<Settlement> settlements) where T : IEntity
{
var connE = KiviiContext.GetOpenedDbConnection<EntitySettlement<T>>();
#region 合并数据库连接,并启用事务
IDbTransaction tranE = null;
if (connE.ConnectionString == conn.ConnectionString)
{
connE.Close();
connE = conn;
}
else
{
tranE = connE.OpenTransaction();
}
#endregion
var instance = Activator.CreateInstance(typeof(T)) as IEntityHasPayment;
foreach (var item in settlements)
{
try
{
var settlement = new EntitySettlement<T>();
settlement.PopulateInstance(item);
settlement.AddOnlyProperties(o => o.PayKvid);
settlement.AddOnlyProperties(o => o.AmountPayment);
connE.UpdateOnly(settlement);
if (instance != null)
{
instance.Kvid = settlement.BizKvid;
instance.AmountPayment = settlement.AmountPayment;
instance.AddOnlyProperties(o => o.AmountPayment);
connE.UpdateOnly(instance);
}
}
catch
{
continue;
}
}
return tranE;
}
}
[Api(Description = "作废登记")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PayOffset : RestfulExecution<Pay>
{
public Guid Kvid { get; set; }
public string Remark { get; set; }
private static MethodInfo _realMethod = null;
public override object OnExecution(IRequest req, IResponse res)
{
Kvid.ThrowIfEmpty("未传入要冲账的付款登记!");
var conn = KiviiContext.GetOpenedDbConnection<Pay>();
var pay = conn.SingleById<Pay>(Kvid);
pay.ThrowIfNull("未找到对应数据!");
if (pay.OffsetKvid != Guid.Empty) throw new Exception("已冲账,请勿重复重复操作!");
if (pay.BizKvid != Guid.Empty) throw new Exception("已关联业务,请先解除关联!");
var settlements = conn.Select<Settlement>(o => o.OffsetKvid == Guid.Empty && o.PayKvid == Kvid);//付款登记的结算
var dynamicPayments = conn.Select<Payment>(o => o.OffsetKvid == Guid.Empty && o.BizKvid == Kvid);//使用的付款信息 要作废
List<Payment> offsetPayments = null;//要作废的关联数据
List<Payment> payPayments = null;//要作废的关联数据
List<AccountDetail> allAccountDetails = null;//退回对应账户生成的明细
var offsetPay = pay.Offset($"付款登记作废,:{Remark}");
Account payerAccount = null;
Account payeeAccount = null;
if (!dynamicPayments.IsNullOrEmpty())
{
dynamicPayments.Exists(o => o.Type != PaymentType.DynamicSplit).ThrowIfTrue("存在不正确的付款数据类型!");
dynamicPayments.Exists(o => o.PayerAccountKvid == Guid.Empty || o.PayeeAccountKvid == Guid.Empty).ThrowIfTrue("存在无收付账户信息的记录!");
payPayments = conn.Select<Payment>(o => o.OffsetKvid == Guid.Empty && Sql.In(o.ParentKvid, dynamicPayments.ConvertAll(p => p.Kvid)));
foreach (var dynamicPayment in dynamicPayments)
{
if (payerAccount == null || payerAccount.Kvid != dynamicPayment.PayerAccountKvid)
{
payerAccount = conn.SingleById<Account>(dynamicPayment.PayerAccountKvid);
payerAccount.ThrowIfNull("未找到当前对应账户");
}
if (payeeAccount == null || payeeAccount.Kvid != dynamicPayment.PayeeAccountKvid)
{
payeeAccount = conn.SingleById<Account>(dynamicPayment.PayeeAccountKvid);
payeeAccount.ThrowIfNull("未找到当前对应账户");
}
var accountDetails = dynamicPayment.Using(payeeAccount, payerAccount, $"付款登记作废,:{Remark}");//原路退回对应账户
if (allAccountDetails.IsNullOrEmpty()) allAccountDetails = new List<AccountDetail>();
allAccountDetails.AddRange(accountDetails);
var offsetPayment = dynamicPayment.Offset($"付款登记作废:{Remark}");
if (offsetPayments.IsNullOrEmpty()) offsetPayments = new List<Payment>();
offsetPayments.Add(offsetPayment);
}
}
if (!payPayments.IsNullOrEmpty())
{
foreach (var item in payPayments)
{
var offsetPayment = item.Offset($"付款登记作废:{Remark}");
if (offsetPayments.IsNullOrEmpty()) offsetPayments = new List<Payment>();
offsetPayments.Add(offsetPayment);
}
}
var rtns = new RestfulExecutionResponse<Pay>();
rtns.Results = new List<Pay>();
var trans = conn.OpenTransaction();
List<IDbTransaction> subTrans = new List<IDbTransaction>();
try
{
if (!payPayments.IsNullOrEmpty()) payPayments.ForEach(o => conn.UpdateOnly(o));
if (!offsetPayments.IsNullOrEmpty()) offsetPayments.ForEach(o => conn.Insert(o));
if (!dynamicPayments.IsNullOrEmpty())
{
dynamicPayments.ForEach(o => conn.UpdateOnly(o));
foreach (var item in dynamicPayments)
{
if (item.RootKvid != item.Kvid) item.RecalculateAmountUsed(conn);
}
}
if (!allAccountDetails.IsNullOrEmpty()) allAccountDetails.ForEach(o => o.Insert(conn));
if (!settlements.IsNullOrEmpty())
{
var updateLamda = conn.From<Settlement>();
updateLamda = updateLamda.Update(o => new { o.PayKvid, o.AmountPayment });
updateLamda = updateLamda.Where(o => Sql.In(o.Kvid, settlements.ConvertAll(p => p.Kvid)));
conn.UpdateOnly<Settlement>(new Settlement { PayKvid = Guid.Empty, AmountPayment = 0 }, updateLamda);
var groupSettlement = settlements.GroupBy(o => o.BizType);
if (_realMethod == null)
{
var function = new Func<IDbConnection, List<Settlement>, object>(onExecution<Entity>);
_realMethod = this.GetType().GetMethod(function.Method.Name, BindingFlags.NonPublic | BindingFlags.Instance);
}
foreach (var group in groupSettlement)
{
var entityType = Text.AssemblyUtils.FindType(group.Key);
if (entityType == null) continue;
if (entityType == typeof(Settlement)) continue;
var result = _realMethod.MakeGenericMethod(entityType).Invoke(this, new object[2] { conn, group.ToList() });
if (result != null) subTrans.Add(result as IDbTransaction);
}
}
conn.UpdateOnly(pay);
rtns.Results.Add(pay);
conn.Insert(offsetPay);
trans.Commit();
subTrans.ForEach(o => o?.Commit());
}
catch (Exception ex)
{
trans.Rollback();
subTrans.ForEach(o => o?.Rollback());
throw ex;
}
return rtns;
}
private IDbTransaction onExecution<T>(IDbConnection conn, List<Settlement> settlements) where T : IEntity
{
var connE = KiviiContext.GetOpenedDbConnection<EntitySettlement<T>>();
#region 合并数据库连接,并启用事务
IDbTransaction tranE = null;
if (connE.ConnectionString == conn.ConnectionString)
{
connE.Close();
connE = conn;
}
else
{
tranE = connE.OpenTransaction();
}
#endregion
var updateLamda = connE.From<EntitySettlement<T>>();
updateLamda = updateLamda.Update(o => new { o.PayKvid, o.AmountPayment });
updateLamda = updateLamda.Where(o => Sql.In(o.Kvid, settlements.ConvertAll(p => p.Kvid)));
connE.UpdateOnly<EntitySettlement<T>>(new EntitySettlement<T> { PayKvid = Guid.Empty, AmountPayment = 0 }, updateLamda);
var instance = Activator.CreateInstance(typeof(T)) as IEntityHasPayment;
foreach (var item in settlements)
{
try
{
if (instance != null)
{
instance.Kvid = item.BizKvid;
instance.AmountPayment = 0;
instance.AddOnlyProperties(o => o.AmountPayment);
connE.UpdateOnly(instance);
}
}
catch
{
continue;
}
}
return tranE;
}
}
[Api(Description = "更新")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PayUpdate : RestfulExecution<Pay>
{
public Pay Item { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Item == null).ThrowIfTrue("参数错误:未传入付款单信息!");
(Item.Kvid == Guid.Empty).ThrowIfTrue("参数错误:未传入付款单Kvid!");
var conn = KiviiContext.GetOpenedDbConnection<Pay>();
var pay = conn.SingleById<Pay>(Item.Kvid);
(pay == null).ThrowIfTrue("未找到相应付款单信息!");
if (Item.OnlyPropertiesIsExist(o => o.Summary))
{
pay.Summary = Item.Summary;
pay.AddOnlyProperties(o => o.Summary);
}
if (Item.OnlyPropertiesIsExist(o => o.Remark))
{
pay.Remark = Item.Remark;
pay.AddOnlyProperties(o => o.Remark);
}
var rtns = new RestfulUpdateResponse<Pay>();
rtns.Results = new List<Pay>();
if (pay.OnlyProperties.Count > 0)
{
conn.UpdateOnly(pay);
rtns.Results.Add(pay);
}
return rtns;
}
}
[RequiresAnyRole(SystemRoles.Everyone)]
public class PayRead : RestfulRead<Pay> { }
[Api(Description = "查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PayQuery : RestfulExecution<Pay>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<Pay>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Pay>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "生成到账流水")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentAccept : RestfulExecution<Payment>
{
public Payment Item { get; set; }
public List<Payment> Items { get; set; }
[ApiMember(Description = "认领类型,默认为Department,Organization,Member")]
public OwnerType OwnerType { get; set; } = OwnerType.Department;
public override object OnExecution(IRequest req, IResponse res)
{
(Item == null && Items.IsNullOrEmpty()).ThrowIfTrue("Need Item Or Items!");
if (Items.IsNullOrEmpty()) Items = new List<Payment>();
if (Item != null) Items.Add(Item);
if (Items.Exists(o => o.Currency != CurrencyUnit.CNY)) throw new Exception("暂仅支持生成人民币货币单位流水!");
var typeBanks = Items.Where(o => o.Type == PaymentType.Bank).ToList();
var typeCashs = Items.Where(o => o.Type == PaymentType.Cash).ToList();
var typePoss = Items.Where(o => o.Type == PaymentType.Pos).ToList();
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var accountBalance = CurrencyUnit.CNY.GetBalanceAccount();
var allAccountDetails = new List<AccountDetail>();
var allAcceptPayments = new List<Payment>();
//生成银行流水
if (!typeBanks.IsNullOrEmpty())
{
var bankAccounts = conn.Select<Account>(o => o.Type == AccountType.Bank && o.Currency == CurrencyUnit.CNY && o.OwnerKvid == KiviiContext.CurrentMember.OrganizationKvid);
if (bankAccounts.IsNullOrEmpty()) throw new Exception("未找到当前登录人所在组织的银行账户!");
foreach (var item in typeBanks)
{
(item.PayeeAccountSerialNumber.IsNullOrEmpty()).ThrowIfTrue("银行流水收款单位PayeeAccountSerialNumber不能为空");
var bankAccount = bankAccounts.FirstOrDefault(o => o.SerialNumber == item.PayeeAccountSerialNumber);
if (bankAccount == null) throw new Exception($"未找到当前银行流水{item.PayerName}[{item.PayeeAccountSerialNumber}]对应系统银行账户!");
if (item.Amount <= 0) continue;
var itemKvid = Guid.NewGuid();
item.AmountInvoice = 0;
item.AmountSplited = 0;
item.AmountUsed = 0;
item.OwnerKvid = KiviiContext.CurrentMember.OrganizationKvid;
item.OwnerName = KiviiContext.CurrentMember.OrganizationName;
item.OffsetKvid = Guid.Empty;
item.BizId = string.Empty;
item.BizType = string.Empty;
item.BizKvid = Guid.Empty;
item.Kvid = itemKvid;
item.RootKvid = item.Kvid;
item.ParentKvid = Guid.Empty;
item.PayerAccountKvid = accountBalance.Kvid;
item.PayerAccountName = accountBalance.Name;
item.PayerAccountOwnerName = accountBalance.OwnerName;
item.PayerAccountSerialNumber = accountBalance.SerialNumber;
item.PayeeAccountKvid = bankAccount.Kvid;
item.PayeeAccountName = bankAccount.Name;
item.PayeeAccountOwnerName = bankAccount.OwnerName;
item.PayeeAccountSerialNumber = bankAccount.SerialNumber;
item.OperateTime = (item.OperateTime == DateTime.MinValue ? DateTime.Now : item.OperateTime);
item.OperatorName = KiviiContext.CurrentMember.FullName;
item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
var accountDetails = item.Accept();
allAcceptPayments.Add(item);
allAccountDetails.AddRange(accountDetails);
}
}
var ownerKvid = Guid.Empty;
var ownerName = string.Empty;
switch (OwnerType)
{
case OwnerType.Organization:
{
ownerKvid = KiviiContext.CurrentMember.OrganizationKvid;
ownerName = KiviiContext.CurrentMember.OrganizationName;
break;
}
case OwnerType.Member:
{
ownerKvid = KiviiContext.CurrentMember.Kvid;
ownerName = KiviiContext.CurrentMember.FullName;
break;
}
default:
{
ownerKvid = KiviiContext.CurrentMember.DepartmentKvid;
ownerName = KiviiContext.CurrentMember.DepartmentName;
break;
}
}
//生成现金流水
if (!typeCashs.IsNullOrEmpty())
{
var cashAccount = CurrencyUnit.CNY.GetPersonalAccount(AccountType.Cash);
if (cashAccount == null) throw new Exception("未找到当前登录人的现金账户!");
foreach (var item in typeCashs)
{
if (item.Amount <= 0) continue;
var itemKvid = Guid.NewGuid();
item.AmountInvoice = 0;
item.AmountSplited = 0;
item.AmountUsed = 0;
item.OwnerKvid = ownerKvid;
item.OwnerName = ownerName;
item.OffsetKvid = Guid.Empty;
item.BizId = string.Empty;
item.BizType = string.Empty;
item.BizKvid = Guid.Empty;
item.Kvid = itemKvid;
item.RootKvid = item.Kvid;
item.ParentKvid = Guid.Empty;
item.PayerAccountKvid = cashAccount.Kvid;
item.PayerAccountName = cashAccount.Name;
item.PayerAccountOwnerName = cashAccount.OwnerName;
item.PayerAccountSerialNumber = cashAccount.SerialNumber;
item.PayeeAccountKvid = accountBalance.Kvid;
item.PayeeAccountName = accountBalance.Name;
item.PayeeAccountOwnerName = accountBalance.OwnerName;
item.PayeeAccountSerialNumber = accountBalance.SerialNumber;
item.OperateTime = (item.OperateTime == DateTime.MinValue ? DateTime.Now : item.OperateTime);
item.OperatorName = KiviiContext.CurrentMember.FullName;
item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
var accountDetails = item.Accept();
allAcceptPayments.Add(item);
allAccountDetails.AddRange(accountDetails);
}
}
//生成Pos流水
if (!typePoss.IsNullOrEmpty())
{
var posAccount = CurrencyUnit.CNY.GetPersonalAccount(AccountType.Pos);
if (posAccount == null) throw new Exception("未找到当前登录人的刷卡账户!");
foreach (var item in typePoss)
{
if (item.Amount <= 0) continue;
var itemKvid = Guid.NewGuid();
item.AmountInvoice = 0;
item.AmountSplited = 0;
item.AmountUsed = 0;
item.OwnerKvid = ownerKvid;
item.OwnerName = ownerName;
item.OffsetKvid = Guid.Empty;
item.BizId = string.Empty;
item.BizType = string.Empty;
item.BizKvid = Guid.Empty;
item.Kvid = itemKvid;
item.RootKvid = item.Kvid;
item.ParentKvid = Guid.Empty;
item.PayerAccountKvid = posAccount.Kvid;
item.PayerAccountName = posAccount.Name;
item.PayerAccountOwnerName = posAccount.OwnerName;
item.PayerAccountSerialNumber = posAccount.SerialNumber;
item.PayeeAccountKvid = accountBalance.Kvid;
item.PayeeAccountName = accountBalance.Name;
item.PayeeAccountOwnerName = accountBalance.OwnerName;
item.PayeeAccountSerialNumber = accountBalance.SerialNumber;
item.OperateTime = (item.OperateTime == DateTime.MinValue ? DateTime.Now : item.OperateTime);
item.OperatorName = KiviiContext.CurrentMember.FullName;
item.OperatorKvid = KiviiContext.CurrentMember.Kvid;
var accountDetails = item.Accept();
allAcceptPayments.Add(item);
allAccountDetails.AddRange(accountDetails);
}
}
var rtns = new RestfulCreateResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
if (!allAcceptPayments.IsNullOrEmpty())
{
foreach (var item in allAcceptPayments)
{
conn.Insert(item);
rtns.Results.Add(item);
}
}
if (!allAccountDetails.IsNullOrEmpty()) allAccountDetails.ForEach(o => o.Insert(conn));
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
/// <summary>
/// 此接口用于以下情况
/// 1.到账充值到客户存款账户
/// 2.退款存入退款账户
/// 3.非业务收入存入个人现金,刷卡等账户
/// </summary>
[Api(Description = "收款转账")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentTransfer : RestfulExecution<Payment>
{
public Guid AccountKvid { get; set; }
/// <summary>
/// 类型:Split,UnBiz,Refund,Cash,Pos,AliPay,Wechat
/// </summary>
public Guid PaymentKvid { get; set; }
public decimal Amount { get; set; }
public string Remark { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (AccountKvid == Guid.Empty || PaymentKvid == Guid.Empty) throw new Exception("请选择要处理的到账信息!");
if (Amount <= 0) throw new Exception("入账金额不能小于等0!");
var conn = KiviiContext.GetOpenedDbConnection<Account>();
var payment = conn.SingleById<Payment>(PaymentKvid);
if (payment == null) throw new Exception("不存在的收款信息!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("此收款已冲账!");
if (payment.Type != PaymentType.UnBiz && payment.Type != PaymentType.Refund && payment.Type != PaymentType.Split && payment.Type != PaymentType.Cash && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat && payment.Type != PaymentType.Pos) throw new Exception("不支持收款充值操作类型!");
if (payment.Amount - payment.AmountUsed < Amount) throw new Exception($"此到账可使用金额不足,仅剩:{payment.Amount - payment.AmountUsed}元");
var accountPayee = conn.SingleById<Account>(AccountKvid);
if (accountPayee == null) throw new Exception("不存在此账户");
if (accountPayee.Type != AccountType.Cash && accountPayee.Type != AccountType.Pos && accountPayee.Type != AccountType.Deposit && accountPayee.Type != AccountType.Refund) throw new Exception("不支持的账户类型");
var accountPayer = accountPayee.Currency.GetBalanceAccount();
accountPayer.ThrowIfNull($"未能正确获取系统{accountPayee.Currency}平衡账户!请联系管理员!");
//生成转账Payment
Payment transfer = null;
//生成账户明细
List<AccountDetail> accountDetails = new List<AccountDetail>();
//存在的父级Payment要更新AmountUsed
Payment parentPayment = null;
//3.
if (payment.Type == PaymentType.UnBiz)
{
if (accountPayee.Type != AccountType.Cash && accountPayee.Type != AccountType.Pos) throw new Exception("非业务收款,仅支持现金或刷卡个人账户充值!");
transfer = payment.Transfer(accountPayee, accountPayer, Amount, $"非业务入账{(Remark.IsNullOrEmpty() ? string.Empty : ":" + Remark)}");
accountDetails = transfer.Using(accountPayer, accountPayee);
}
//2.
else if (payment.Type == PaymentType.Refund)
{
if (accountPayee.Type != AccountType.Refund) throw new Exception("退款收款,仅支持退款账户充值!");
transfer = payment.Transfer(accountPayee, accountPayer, Amount, $"退款入账{(Remark.IsNullOrEmpty() ? string.Empty : ":" + Remark)}");
accountDetails = transfer.Using(accountPayer, accountPayee);
}
//1.
else
{
if(accountPayee.Type!=AccountType.Deposit) throw new Exception("业务收款,仅支持客户存款账户充值!");
transfer = payment.Transfer(accountPayee, accountPayer, Amount, $"到账充值{(Remark.IsNullOrEmpty() ? string.Empty : ":" + Remark)}");
accountDetails = transfer.Using(accountPayer, accountPayee);
}
if (payment.ParentKvid != Guid.Empty) parentPayment = conn.SingleById<Payment>(payment.ParentKvid);
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
if (transfer != null)
{
conn.Insert(transfer);
rtns.Results.Add(transfer);
}
accountDetails.ForEach(o => o.Insert(conn));
conn.UpdateOnly(payment);
if (parentPayment != null)
{
parentPayment.AmountUsed += transfer.Amount;
parentPayment.AddOnlyProperties(o => o.AmountUsed);
conn.UpdateOnly(parentPayment);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
//(重点测试下2021.09.14)
[Api(Description = "收款转账撤销")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentTransferCancel : RestfulExecution<Payment>
{
/// <summary>
/// 类型:Transfer
/// </summary>
public List<Guid> PaymentKvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
PaymentKvids.ThrowIfNullOrEmpty("请传入要撤销转账的内容!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(PaymentKvids);
payments.ThrowIfNullOrEmpty("未找到要撤销转账的信息!");
(payments.Count != PaymentKvids.Count).ThrowIfTrue("所选到账与查询到数据数量不符合!");
payments.Exists(o => o.Type != PaymentType.Transfer).ThrowIfTrue("存在不正确的到账类型!");
payments.Exists(o => o.OffsetKvid != Guid.Empty).ThrowIfTrue("存在已撤销的转账!");
payments.Exists(o => o.ParentKvid == Guid.Empty).ThrowIfTrue("存在不完整的转账数据!");
var parentPayments = conn.SelectByIds<Payment>(payments.ConvertAll(o => o.ParentKvid));
parentPayments.ThrowIfNullOrEmpty("未找到夫级付款,数据错误!");
var rootPayments = conn.SelectByIds<Payment>(parentPayments.ConvertAll(o => o.ParentKvid));
var accountPayees = conn.SelectByIds<Account>(payments.ConvertAll(o => o.PayeeAccountKvid));
accountPayees.ThrowIfNullOrEmpty("未找到收款账户!");
var accountPayers = conn.SelectByIds<Account>(payments.ConvertAll(o => o.PayerAccountKvid));
accountPayers.ThrowIfNullOrEmpty("未找到付款账户!");
//新生成的Offset数据
var offsets = new List<Payment>();
//原Transfer数据更新Offset
var orginOffsets = new List<Payment>();
//生成的账户明细
var totalAccountDetails = new List<AccountDetail>();
//Parent和Root 更新AmountUsed
var roots = new List<Payment>();
foreach (var item in payments)
{
var parent = parentPayments.FirstOrDefault(o => o.Kvid == item.ParentKvid);
parent.ThrowIfNull($"未找到{item.SerialNumber}当前的到账上级节点!");
var root = rootPayments.FirstOrDefault(o => o.Kvid == parent.ParentKvid);
var accountPayee = accountPayees.FirstOrDefault(o => o.Kvid == item.PayeeAccountKvid);
accountPayee.ThrowIfNull($"未找到{item.SerialNumber}对应收款账户!");
var accountPayer = accountPayers.FirstOrDefault(o => o.Kvid == item.PayerAccountKvid);
accountPayer.ThrowIfNull($"未找到{item.SerialNumber}对应付款账户!");
#region 新增流水
var payeeAaccountDetail = new AccountDetail();
payeeAaccountDetail.AccountKvid = accountPayee.Kvid;
payeeAaccountDetail.BizId = item.SerialNumber;
payeeAaccountDetail.BizKvid = item.Kvid;
payeeAaccountDetail.BizType = typeof(Payment).FullName;
payeeAaccountDetail.PayerKvid = item.PayeeKvid;
payeeAaccountDetail.PayerName = item.PayeeName;
payeeAaccountDetail.PayerAccountKvid = accountPayee.Kvid;
payeeAaccountDetail.PayerAccountName = accountPayee.Name;
payeeAaccountDetail.PayerAccountSerialNumber = accountPayee.SerialNumber;
payeeAaccountDetail.PayeeKvid = item.PayerKvid;
payeeAaccountDetail.PayeeName = item.PayerName;
payeeAaccountDetail.PayeeAccountKvid = accountPayer.Kvid;
payeeAaccountDetail.PayeeAccountName = accountPayer.Name;
payeeAaccountDetail.PayeeAccountSerialNumber = accountPayer.SerialNumber;
payeeAaccountDetail.AmountPayment = item.Amount;
payeeAaccountDetail.Summary = "撤销转账";
#endregion
totalAccountDetails.Add(payeeAaccountDetail);
#region 新增流水
var payerAaccountDetail = new AccountDetail();
payerAaccountDetail.AccountKvid = accountPayer.Kvid;
payerAaccountDetail.BizId = item.SerialNumber;
payerAaccountDetail.BizKvid = item.Kvid;
payerAaccountDetail.BizType = typeof(Payment).FullName;
payerAaccountDetail.PayerKvid = item.PayeeKvid;
payerAaccountDetail.PayerName = item.PayeeName;
payerAaccountDetail.PayerAccountKvid = accountPayee.Kvid;
payerAaccountDetail.PayerAccountName = accountPayee.Name;
payerAaccountDetail.PayerAccountSerialNumber = accountPayee.SerialNumber;
payerAaccountDetail.PayeeKvid = item.PayerKvid;
payerAaccountDetail.PayeeName = item.PayerName;
payerAaccountDetail.PayeeAccountKvid = accountPayer.Kvid;
payerAaccountDetail.PayeeAccountName = accountPayer.Name;
payerAaccountDetail.PayeeAccountSerialNumber = accountPayer.SerialNumber;
payerAaccountDetail.AmountPayment = item.Amount;
payerAaccountDetail.Summary = "撤销转账";
#endregion
totalAccountDetails.Add(payerAaccountDetail);
var offset = item.Offset($"撤销转账");
offsets.Add(offset);
orginOffsets.Add(item);
if (roots.Exists(o => o.Kvid == parent.Kvid))
{
var exist = roots.FirstOrDefault(o => o.Kvid == parent.Kvid);
exist.AmountUsed -= item.Amount;
}
else
{
parent.AmountUsed -= item.Amount;
parent.AddOnlyProperties(o => o.AmountUsed);
roots.Add(parent);
}
if (root != null)
{
if (roots.Exists(o => o.Kvid == root.Kvid))
{
var exist = roots.FirstOrDefault(o => o.Kvid == root.Kvid);
exist.AmountUsed -= item.Amount;
}
else
{
root.AmountUsed -= item.Amount;
root.AddOnlyProperties(o => o.AmountUsed);
roots.Add(root);
}
}
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
foreach(var offset in offsets)
{
conn.Insert(offset);
}
foreach (var item in orginOffsets)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
foreach(var item in totalAccountDetails)
{
item.Insert(conn);
}
foreach( var item in roots)
{
conn.UpdateOnly(item);
}
trans.Commit();
}
catch(Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "到账冲账")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentOffset : RestfulExecution<Payment>
{
public Guid Kvid { get; set; }
public string Remark { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Kvid == Guid.Empty).ThrowIfTrue("请传入要冲账的到账Kvid!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payment = conn.SingleById<Payment>(Kvid);
if (payment == null) throw new Exception("收付款项不存在");
if (payment.Type != PaymentType.Bank && payment.Type != PaymentType.Cash && payment.Type != PaymentType.Pos && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat) throw new Exception("非可冲销收款类型!");
if (payment.BizKvid != Guid.Empty) throw new Exception("收付款已关联业务!请先解除关联!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("收付款已冲账");
if (payment.AmountInvoice > 0 || payment.AmountSplited > 0 || payment.AmountUsed > 0) throw new Exception($"不可冲账,此到账已开票:{payment.AmountInvoice}元,已认领:{payment.AmountSplited}元,已使用:{payment.AmountUsed}元");
//查询当前项的拆分子项List
if (conn.Exists<Payment>(o => o.ParentKvid == Kvid && o.OffsetKvid == Guid.Empty)) throw new Exception("不可冲账,此到账存在未冲账子项!");
if (conn.Exists<InvoiceApply>(o => o.BizKvid == payment.Kvid && o.OffsetKvid == Guid.Empty)) throw new Exception("申请开票中,不可冲账!");
payment.PayeeAccountKvid.ThrowIfEmpty("收款账户(PayeeAccountKvid)为空!");
payment.PayerAccountKvid.ThrowIfEmpty("付款账户(PayerAccountKvid)为空!");
var accountPayee = conn.SingleById<Account>(payment.PayeeAccountKvid);
var accountPayer = conn.SingleById<Account>(payment.PayerAccountKvid);
if (accountPayee == null) accountPayee = payment.Currency.GetBalanceAccount();
if (accountPayer == null) accountPayer = payment.Currency.GetBalanceAccount();
var offset = payment.Offset($"冲账:{Remark}");
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
using (var trans = conn.OpenTransaction())
{
conn.UpdateOnly(payment);
rtns.Results.Add(payment);
conn.Insert(offset);
if (accountPayee != null)
{
#region 新增流水
var newAccountDetail = new AccountDetail();
newAccountDetail.AccountKvid = accountPayee.Kvid;
newAccountDetail.BizId = payment.SerialNumber;
newAccountDetail.BizKvid = payment.Kvid;
newAccountDetail.BizType = typeof(Payment).FullName;
newAccountDetail.PayerKvid = payment.PayeeKvid;
newAccountDetail.PayerName = payment.PayeeName;
newAccountDetail.PayerAccountKvid = accountPayee.Kvid;
newAccountDetail.PayerAccountName = accountPayee.Name;
newAccountDetail.PayerAccountSerialNumber = accountPayee.SerialNumber;
newAccountDetail.PayeeKvid = payment.PayerKvid;
newAccountDetail.PayeeName = payment.PayerName;
newAccountDetail.PayeeAccountKvid = accountPayer.Kvid;
newAccountDetail.PayeeAccountName = accountPayer.Name;
newAccountDetail.PayeeAccountSerialNumber = accountPayer.SerialNumber;
newAccountDetail.AmountPayment = payment.Amount;
newAccountDetail.Summary = "冲账:" + Remark;
#endregion
newAccountDetail.Insert(conn);
}
if (accountPayer != null)
{
#region 新增流水
var accountDetailBalance = new AccountDetail();
accountDetailBalance.AccountKvid = accountPayer.Kvid;
accountDetailBalance.BizId = payment.SerialNumber;
accountDetailBalance.BizKvid = payment.Kvid;
accountDetailBalance.BizType = typeof(Payment).FullName;
accountDetailBalance.PayerKvid = payment.PayeeKvid;
accountDetailBalance.PayerName = payment.PayeeName;
accountDetailBalance.PayerAccountKvid = accountPayee.Kvid;
accountDetailBalance.PayerAccountName = accountPayee.Name;
accountDetailBalance.PayerAccountSerialNumber = accountPayee.SerialNumber;
accountDetailBalance.PayeeKvid = payment.PayerKvid;
accountDetailBalance.PayeeName = payment.PayerName;
accountDetailBalance.PayeeAccountKvid = accountPayer.Kvid;
accountDetailBalance.PayeeAccountName = accountPayer.Name;
accountDetailBalance.PayeeAccountSerialNumber = accountPayer.SerialNumber;
accountDetailBalance.AmountPayment = payment.Amount;
accountDetailBalance.Summary = "冲账:" + Remark;
#endregion
accountDetailBalance.Insert(conn);
}
trans.Commit();
}
return rtns;
}
}
[Api(Description = "到账冲账撤销")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentOffsetCancel : RestfulExecution<Payment>
{
public Guid Kvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
(Kvid == Guid.Empty).ThrowIfTrue("请传入要撤销冲账的到账Kvid!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payment = conn.SingleById<Payment>(Kvid);
if (payment == null) throw new Exception("收付款项不存在");
if (payment.Type != PaymentType.Bank && payment.Type != PaymentType.Cash && payment.Type != PaymentType.Pos && payment.Type != PaymentType.AliPay && payment.Type != PaymentType.WeChat) throw new Exception("不支持的操作类型!");
if (payment.OffsetKvid == Guid.Empty) throw new Exception("收付款未冲账,无需撤销");
if (payment.BizKvid != Guid.Empty) throw new Exception("收付款已关联业务!请先解除关联!");
payment.PayeeAccountKvid.ThrowIfEmpty("收款账户(PayeeAccountKvid)为空!");
payment.PayerAccountKvid.ThrowIfEmpty("付款账户(PayerAccountKvid)为空!");
var offset = conn.SingleById<Payment>(payment.OffsetKvid);
var accountPayee = conn.SingleById<Account>(payment.PayeeAccountKvid);
var accountPayer = conn.SingleById<Account>(payment.PayerAccountKvid);
if (accountPayee == null) accountPayee = payment.Currency.GetBalanceAccount();
if (accountPayer == null) accountPayer = payment.Currency.GetBalanceAccount();
//payment 取消OffsetKvid
payment.OffsetKvid = Guid.Empty;
payment.AddOnlyProperties(o => o.OffsetKvid);
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
using (var trans = conn.OpenTransaction())
{
conn.UpdateOnly(payment);
rtns.Results.Add(payment);
if (offset != null)
{
offset.Status = -1;
offset.AddOnlyProperties(o => o.Status);
conn.UpdateOnly(offset);
}
if (accountPayee != null)
{
#region 新增流水
var newAccountDetail = new AccountDetail();
newAccountDetail.AccountKvid = accountPayee.Kvid;
newAccountDetail.BizId = payment.SerialNumber;
newAccountDetail.BizKvid = payment.Kvid;
newAccountDetail.BizType = typeof(Payment).FullName;
newAccountDetail.PayeeKvid = payment.PayeeKvid;
newAccountDetail.PayeeName = payment.PayeeName;
newAccountDetail.PayeeAccountKvid = accountPayee.Kvid;
newAccountDetail.PayeeAccountName = accountPayee.Name;
newAccountDetail.PayeeAccountSerialNumber = accountPayee.SerialNumber;
newAccountDetail.PayerKvid = payment.PayerKvid;
newAccountDetail.PayerName = payment.PayerName;
newAccountDetail.PayerAccountKvid = accountPayer.Kvid;
newAccountDetail.PayerAccountName = accountPayer.Name;
newAccountDetail.PayerAccountSerialNumber = accountPayer.SerialNumber;
newAccountDetail.AmountPayment = payment.Amount;
newAccountDetail.Summary = "撤销冲账";
#endregion
newAccountDetail.Insert(conn);
}
if (accountPayer != null)
{
#region 新增流水
var accountDetailBalance = new AccountDetail();
accountDetailBalance.AccountKvid = accountPayer.Kvid;
accountDetailBalance.BizId = payment.SerialNumber;
accountDetailBalance.BizKvid = payment.Kvid;
accountDetailBalance.BizType = typeof(Payment).FullName;
accountDetailBalance.PayeeKvid = payment.PayeeKvid;
accountDetailBalance.PayeeName = payment.PayeeName;
accountDetailBalance.PayeeAccountKvid = accountPayee.Kvid;
accountDetailBalance.PayeeAccountName = accountPayee.Name;
accountDetailBalance.PayeeAccountSerialNumber = accountPayee.SerialNumber;
accountDetailBalance.PayerKvid = payment.PayerKvid;
accountDetailBalance.PayerName = payment.PayerName;
accountDetailBalance.PayerAccountKvid = accountPayer.Kvid;
accountDetailBalance.PayerAccountName = accountPayer.Name;
accountDetailBalance.PayerAccountSerialNumber = accountPayer.SerialNumber;
accountDetailBalance.AmountPayment = payment.Amount;
accountDetailBalance.Summary = "撤销冲账";
#endregion
accountDetailBalance.Insert(conn);
}
trans.Commit();
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
public enum OwnerType
{
Department = 0,
Organization = 1,
Member = 2
}
[Api(Description = "业务认领")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentSplit : RestfulExecution<Payment>
{
[ApiMember(Description = "认领金额")]
public decimal Amount { get; set; }
[ApiMember(Description = "认领类型,默认为Department,Organization,Member")]
public OwnerType OwnerType { get; set; } = OwnerType.Department;
public Guid Kvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvid == Guid.Empty) throw new Exception("请选择要认领的收款!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payment = conn.SingleById<Payment>(Kvid);
if (payment == null) throw new Exception("收款不存在!");
if (payment.Type != PaymentType.Bank && payment.Type != PaymentType.Split) throw new Exception("非银行到账不可以认领!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("已冲销收款不可认领!");
var ownerKvid = Guid.Empty;
var ownerName = string.Empty;
switch (OwnerType)
{
case OwnerType.Organization:
{
ownerKvid = KiviiContext.CurrentMember.OrganizationKvid;
ownerName = KiviiContext.CurrentMember.OrganizationName;
break;
}
case OwnerType.Member:
{
ownerKvid = KiviiContext.CurrentMember.Kvid;
ownerName = KiviiContext.CurrentMember.FullName;
break;
}
default:
{
ownerKvid = KiviiContext.CurrentMember.DepartmentKvid;
ownerName = KiviiContext.CurrentMember.DepartmentName;
break;
}
}
Payment bank = null;
Payment split = null;
EntityLog<Payment> log = null;
if (payment.Type == PaymentType.Bank)
{
if (Amount <= 0) throw new Exception("金额不能为小于0!");
if (payment.AmountSplited == payment.Amount) throw new Exception("无剩余收款可认领!");
if (payment.AmountSplited + Amount > payment.Amount) throw new Exception("认领金额超出范围!");
bank = payment;
split = bank.BizSplit(Amount, ownerKvid, ownerName);
#region 记录日志
log = new EntityLog<Payment>();
log.OwnerKvid = bank.Kvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{bank.SerialNumber}的收款进行认领至{ownerName},认领金额为:{split.Amount}元.";
#endregion
}
if (payment.Type == PaymentType.Split)
{
split = payment;
split.OwnerKvid = ownerKvid;
split.AddOnlyProperties(o => o.OwnerKvid);
split.OwnerName = ownerName;
split.AddOnlyProperties(o => o.OwnerName);
#region 记录日志
log = new EntityLog<Payment>();
log.OwnerKvid = split.ParentKvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{split.SerialNumber}的收款进行认领至{ownerName},认领金额为:{split.Amount}元.";
#endregion
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
//开启事务进行数据库操作
var trans = conn.OpenTransaction();
try
{
if (payment.Type == PaymentType.Bank)
{
if (split != null)
{
conn.Insert(split);
rtns.Results.Add(split);
}
if (bank != null) conn.UpdateOnly(bank);
if (log != null) conn.Insert(log);
}
if (payment.Type == PaymentType.Split)
{
if (split != null)
{
conn.UpdateOnly(split);
rtns.Results.Add(split);
}
if (log != null) conn.Insert(log);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "批量业务认领")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentMultiSplit : RestfulExecution<Payment>
{
[ApiMember(Description = "认领类型,默认为Department,Organization,Member")]
public OwnerType OwnerType { get; set; }
public List<Guid> Kvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要认领的收款!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.Bank && o.Type != PaymentType.Split)) throw new Exception("非银行到账不可以认领!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可认领!");
if (payments.Exists(o => o.AmountSplited >= o.Amount)) throw new Exception("无剩余收款可认领!");
var ownerKvid = Guid.Empty;
var ownerName = string.Empty;
switch (OwnerType)
{
case OwnerType.Department:
{
ownerKvid = KiviiContext.CurrentMember.DepartmentKvid;
ownerName = KiviiContext.CurrentMember.DepartmentName;
break;
}
case OwnerType.Organization:
{
ownerKvid = KiviiContext.CurrentMember.OrganizationKvid;
ownerName = KiviiContext.CurrentMember.OrganizationName;
break;
}
case OwnerType.Member:
{
ownerKvid = KiviiContext.CurrentMember.Kvid;
ownerName = KiviiContext.CurrentMember.FullName;
break;
}
}
var banks = new List<Payment>();
var splits = new List<Payment>();
var splitsSetOwner = new List<Payment>();
var logs = new List<EntityLog<Payment>>();
foreach (var payment in payments)
{
if (payment.Type == PaymentType.Bank)
{
var amount = payment.Amount - payment.AmountSplited;
if (amount <= 0) continue;
var splitPayment = payment.BizSplit(amount, ownerKvid, ownerName);
splits.Add(splitPayment);
banks.Add(payment);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.Kvid;
log.BizId = splitPayment.SerialNumber;
log.BizKvid = splitPayment.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行认领至{ownerName},认领金额为:{splitPayment.Amount}元.";
logs.Add(log);
#endregion
}
if (payment.Type == PaymentType.Split)
{
payment.OwnerKvid = ownerKvid;
payment.AddOnlyProperties(o => o.OwnerKvid);
payment.OwnerName = ownerName;
payment.AddOnlyProperties(o => o.OwnerName);
splitsSetOwner.Add(payment);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.ParentKvid;
log.BizId = payment.SerialNumber;
log.BizKvid = payment.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行认领至{ownerName},认领金额为:{payment.Amount}元.";
logs.Add(log);
#endregion
}
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
//开启事务进行数据库操作
var trans = conn.OpenTransaction();
try
{
foreach (var split in splits)
{
conn.Insert(split);
rtns.Results.Add(split);
}
foreach (var item in splitsSetOwner)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
foreach (var item in banks)
{
conn.UpdateOnly(item);
}
foreach (var log in logs)
{
conn.Insert(log);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "认领撤销")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentSplitCancel : RestfulExecution<Payment>
{
public List<Guid> Kvids { get; set; }
public string Remark { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要撤销认领的收款!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.Split)) throw new Exception("非银行到账不可以认领!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可撤销认领!");
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
//if (payments.Exists(o => o.AmountUsed > 0)) throw new Exception("存在到账已使用,不可撤销认领!");
//认领撤销分两种情况.
//第一种:要是要撤销的目标已经关联过业务了被使用了,撤销只会清空所属信息,重新认领则只是赋值所属信息
//第二种:要是没有被业务关联没被使用,撤销则是Offset当前拆分项目,重新认领则会生成新的拆分项
var paymentUnuseds = payments.Where(o => o.AmountUsed == 0).ToList();
var paymentUseds = payments.Where(o => o.AmountUsed > 0).ToList();
#region 已经使用了的撤销
if (!paymentUseds.IsNullOrEmpty())
{
var paymentKvids = paymentUseds.ConvertAll(o => o.Kvid);
var updateLamda = conn.From<Payment>();
updateLamda = updateLamda.Update(o => new { o.OwnerKvid, o.OwnerName });
updateLamda = updateLamda.Where(o => Sql.In(o.Kvid, paymentKvids));
conn.UpdateOnly(new Payment { OwnerKvid = Guid.Empty, OwnerName = string.Empty }, updateLamda);
rtns.Results.AddRange(paymentUseds);
}
#endregion
#region 没有使用的撤销
if (!paymentUnuseds.IsNullOrEmpty())
{
var rootKvids = paymentUnuseds.ConvertAll(o => o.RootKvid).Distinct().ToList();
var roots = conn.SelectByIds<Payment>(rootKvids);
var rootUpdates = new List<Payment>();
var splitOffsets = new List<Payment>();
var offsets = new List<Payment>();
foreach (var split in paymentUnuseds)
{
var root = roots.FirstOrDefault(o => o.Kvid == split.ParentKvid);
if (root == null) continue;
var offset = split.Offset($"认领撤销:{Remark}");
offsets.Add(offset);
splitOffsets.Add(split);
if (root.AmountSplited >= split.Amount) root.AmountSplited -= split.Amount;
else root.AmountSplited = 0;
root.AddOnlyProperties(o => o.AmountSplited);
if (rootUpdates.Exists(o => o.Kvid == root.Kvid)) rootUpdates.RemoveAll(o => o.Kvid == root.Kvid);
rootUpdates.Add(root);
}
var trans = conn.OpenTransaction();
try
{
foreach (var item in rootUpdates)
{
conn.UpdateOnly(item);
}
foreach (var item in splitOffsets)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
foreach (var item in offsets)
{
conn.Insert(item);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
}
#endregion
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "非业务处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentUnBiz : RestfulExecution<Payment>
{
public decimal Amount { get; set; }
public Guid Kvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Amount <= 0) throw new Exception("金额不能为小于0!");
if (Kvid == Guid.Empty) throw new Exception("请选择要非业务收款处理的到账!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payment = conn.SingleById<Payment>(Kvid);
if (payment == null) throw new Exception("收款不存在!");
if (payment.Type != PaymentType.Bank) throw new Exception("非银行到账不可以操作!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("已冲销收款不可操作!");
if (payment.AmountSplited == payment.Amount) throw new Exception("无剩余收款可操作!");
if (payment.AmountSplited + Amount > payment.Amount) throw new Exception("操作金额超出范围!");
var split = payment.UnBizSplit(Amount, PaymentType.UnBiz);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.Kvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行非业务收款操作,金额为:{split.Amount}元.";
#endregion
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
conn.UpdateOnly(payment);
conn.Insert(split);
conn.Insert(log);
rtns.Results.Add(split);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "批量非业务处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentMultiUnBiz : RestfulExecution<Payment>
{
public List<Guid> Kvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要非业务收款处理的到账!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.Bank)) throw new Exception("非银行到账不可以操作!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可操作!");
if (payments.Exists(o => o.AmountSplited >= o.Amount)) throw new Exception("无剩余收款可操作!");
var banks = new List<Payment>();
var splits = new List<Payment>();
var logs = new List<EntityLog<Payment>>();
foreach (var payment in payments)
{
var amount = payment.Amount - payment.AmountSplited;
if (amount <= 0) continue;
var split = payment.UnBizSplit(amount, PaymentType.UnBiz);
banks.Add(payment);
splits.Add(split);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.Kvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行非业务收款操作,金额为:{split.Amount}元.";
logs.Add(log);
#endregion
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
//开启事务进行数据库操作
var trans = conn.OpenTransaction();
try
{
foreach (var split in splits)
{
conn.Insert(split);
rtns.Results.Add(split);
}
foreach (var item in banks)
{
conn.UpdateOnly(item);
}
foreach (var log in logs)
{
conn.Insert(log);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "撤销非业务处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentUnBizCancel : RestfulExecution<Payment>
{
public List<Guid> Kvids { get; set; }
public string Remark { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要撤销非业务的收款!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.UnBiz)) throw new Exception("非银行到账不可以操作!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可撤销操作!");
if (payments.Exists(o => o.AmountUsed > 0)) throw new Exception("存在到账已使用,不可撤销操作!");
var rootKvids = payments.ConvertAll(o => o.RootKvid).Distinct().ToList();
var roots = conn.SelectByIds<Payment>(rootKvids);
var rootUpdates = new List<Payment>();
var splitOffsets = new List<Payment>();
var offsets = new List<Payment>();
foreach (var split in payments)
{
var root = roots.FirstOrDefault(o => o.Kvid == split.ParentKvid);
if (root == null) continue;
var offset = split.Offset($"非业务撤销:{Remark}");
offsets.Add(offset);
splitOffsets.Add(split);
if (root.AmountSplited >= split.Amount) root.AmountSplited -= split.Amount;
else root.AmountSplited = 0;
root.AddOnlyProperties(o => o.AmountSplited);
if (rootUpdates.Exists(o => o.Kvid == root.Kvid)) rootUpdates.RemoveAll(o => o.Kvid == root.Kvid);
rootUpdates.Add(root);
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
foreach (var item in rootUpdates)
{
conn.UpdateOnly(item);
}
foreach (var item in splitOffsets)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
foreach (var item in offsets)
{
conn.Insert(item);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "退款处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentRefund : RestfulExecution<Payment>
{
public decimal Amount { get; set; }
public Guid Kvid { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Amount <= 0) throw new Exception("金额不能为小于0!");
if (Kvid == Guid.Empty) throw new Exception("请选择要退款处理的到账!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payment = conn.SingleById<Payment>(Kvid);
if (payment == null) throw new Exception("收款不存在!");
if (payment.Type != PaymentType.Bank) throw new Exception("非银行到账不可以操作!");
if (payment.OffsetKvid != Guid.Empty) throw new Exception("已冲销收款不可操作!");
if (payment.AmountSplited == payment.Amount) throw new Exception("无剩余收款可操作!");
if (payment.AmountSplited + Amount > payment.Amount) throw new Exception("操作金额超出范围!");
var split = payment.UnBizSplit(Amount, PaymentType.Refund);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.Kvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行退款操作,金额为:{split.Amount}元.";
#endregion
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
conn.UpdateOnly(payment);
conn.Insert(split);
conn.Insert(log);
rtns.Results.Add(split);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "批量退款处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentMultiRefund : RestfulExecution<Payment>
{
public List<Guid> Kvids { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要退款处理的到账!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.Bank)) throw new Exception("非银行到账不可以操作!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可操作!");
if (payments.Exists(o => o.AmountSplited >= o.Amount)) throw new Exception("无剩余收款可操作!");
var banks = new List<Payment>();
var splits = new List<Payment>();
var logs = new List<EntityLog<Payment>>();
foreach (var payment in payments)
{
var amount = payment.Amount - payment.AmountSplited;
if (amount <= 0) continue;
var split = payment.UnBizSplit(amount, PaymentType.Refund);
banks.Add(payment);
splits.Add(split);
#region 记录日志
var log = new EntityLog<Payment>();
log.OwnerKvid = payment.Kvid;
log.BizId = split.SerialNumber;
log.BizKvid = split.Kvid;
log.BizType = typeof(Payment).FullName;
log.Title = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.Remark = KiviiContext.CurrentMember.FullName + $"在 {log.Title} 对流水号为:{payment.SerialNumber}的收款进行退款操作,金额为:{split.Amount}元.";
logs.Add(log);
#endregion
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
//开启事务进行数据库操作
var trans = conn.OpenTransaction();
try
{
foreach (var split in splits)
{
conn.Insert(split);
rtns.Results.Add(split);
}
foreach (var item in banks)
{
conn.UpdateOnly(item);
}
foreach (var log in logs)
{
conn.Insert(log);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "撤销退款处理")]
[RequiresAnyRole(SystemRoles.Administrator, SystemRoles.Manager, SystemRoles.Configuration)]
public class PaymentRefundCancel : RestfulExecution<Payment>
{
public List<Guid> Kvids { get; set; }
public string Remark { get; set; }
public override object OnExecution(IRequest req, IResponse res)
{
if (Kvids.IsNullOrEmpty()) throw new Exception("请选择要撤销退款的收款!");
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var payments = conn.SelectByIds<Payment>(Kvids);
if (payments == null) throw new Exception("收款不存在!");
if (payments.Exists(o => o.Type != PaymentType.Refund)) throw new Exception("非银行到账不可以操作!");
if (payments.Exists(o => o.OffsetKvid != Guid.Empty)) throw new Exception("已冲销收款不可撤销操作!");
if (payments.Exists(o => o.AmountUsed > 0)) throw new Exception("存在到账已使用,不可撤销操作!");
var rootKvids = payments.ConvertAll(o => o.RootKvid).Distinct().ToList();
var roots = conn.SelectByIds<Payment>(rootKvids);
var rootUpdates = new List<Payment>();
var splitOffsets = new List<Payment>();
var offsets = new List<Payment>();
foreach (var split in payments)
{
var root = roots.FirstOrDefault(o => o.Kvid == split.ParentKvid);
if (root == null) continue;
var offset = split.Offset($"退款撤销:{Remark}");
offsets.Add(offset);
splitOffsets.Add(split);
if (root.AmountSplited >= split.Amount) root.AmountSplited -= split.Amount;
else root.AmountSplited = 0;
root.AddOnlyProperties(o => o.AmountSplited);
if (rootUpdates.Exists(o => o.Kvid == root.Kvid)) rootUpdates.RemoveAll(o => o.Kvid == root.Kvid);
rootUpdates.Add(root);
}
var rtns = new RestfulExecutionResponse<Payment>();
rtns.Results = new List<Payment>();
var trans = conn.OpenTransaction();
try
{
foreach (var item in rootUpdates)
{
conn.UpdateOnly(item);
}
foreach (var item in splitOffsets)
{
conn.UpdateOnly(item);
rtns.Results.Add(item);
}
foreach (var item in offsets)
{
conn.Insert(item);
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentQuery : RestfulQuery<Payment>
{
}
[Api(Description = "到账查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentQueryEx : RestfulExecution<Payment>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public bool? IsPayee { get; set; }//查收款还是付款
public bool? IsSplit { get; set; }//查是否已经拆分认领
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Payment>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
sqlExpress.Where(o => o.OffsetKvid == Guid.Empty && Sql.In(o.Type, PaymentType.AliPay, PaymentType.WeChat, PaymentType.Bank, PaymentType.Cash, PaymentType.Pos));
if (IsSplit != null)
{
if (IsSplit.Value) sqlExpress.And(o => o.AmountSplited > 0 && o.AmountSplited == o.Amount);
else sqlExpress.And(o => o.AmountSplited == 0 || o.AmountSplited < o.Amount);
}
if (IsPayee != null)
{
var queryBankAccount = conn.From<Account>();
queryBankAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Bank, AccountType.WeChat, AccountType.AliPay));
queryBankAccount.Select(o => o.Kvid);
var queryCashAccount = conn.From<Account>();
queryCashAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Cash, AccountType.Pos));
queryCashAccount.Select(o => o.Kvid);
if (IsPayee.Value)//收款
{
sqlExpress.And(o => (Sql.In(o.PayeeAccountKvid, queryBankAccount) || Sql.In(o.PayerAccountKvid, queryCashAccount)));
}
else sqlExpress.And(o => Sql.In(o.PayerAccountKvid, queryBankAccount));
}
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
[Api(Description = "到账开票查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentInvoiceApplyQuery : RestfulExecution<Payment>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
//public bool Group { get; set; }//合并分组的
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Payment>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
sqlExpress.Where(o => o.OffsetKvid == Guid.Empty && Sql.In(o.Type, PaymentType.AliPay, PaymentType.WeChat, PaymentType.Cash, PaymentType.Pos, PaymentType.Split));
sqlExpress.And(o => o.AmountInvoice <= 0 && o.AmountInvoice < o.Amount);
var queryInvoiceApplyRelations = conn.From<InvoiceApply>();
queryInvoiceApplyRelations.Where(o => o.OperateType == InvoiceApplyType.Related && o.OffsetKvid == Guid.Empty);
queryInvoiceApplyRelations.Select(o => o.BizKvid);
sqlExpress.And(o => !Sql.In(o.Kvid, queryInvoiceApplyRelations));
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
[Api(Description = "到账冲销查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class PaymentInvoiceDebitQuery : RestfulExecution<Payment>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public override object OnExecution(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<Payment>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<Payment>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
sqlExpress.Where(o => o.OffsetKvid == Guid.Empty && Sql.In(o.Type, PaymentType.AliPay, PaymentType.WeChat, PaymentType.Bank, PaymentType.Cash, PaymentType.Pos) && o.AmountInvoice < o.Amount);
var queryBankAccount = conn.From<Account>();
queryBankAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Bank, AccountType.WeChat, AccountType.AliPay));
queryBankAccount.Select(o => o.Kvid);
var queryCashAccount = conn.From<Account>();
queryCashAccount.Where(o => o.OrganizationKvid == KiviiContext.CurrentMember.OrganizationKvid && Sql.In(o.Type, AccountType.Cash, AccountType.Pos));
queryCashAccount.Select(o => o.Kvid);
sqlExpress.And(o => (Sql.In(o.PayeeAccountKvid, queryBankAccount) || Sql.In(o.PayerAccountKvid, queryCashAccount)));
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
}
using Kivii.Finances.Entities;
using Kivii.Linq;
using Kivii.Web;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kivii.Finances.Transforms
{
[Api(Description = "泛型结算创建")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementCreate : RestfulExecutionGeneric<Settlement>
{
public Settlement Item { get; set; }
public List<Settlement> Items { get; set; }
public override object OnExecutionGeneric<G>(IRequest req, IResponse res)
{
(Item == null && Items.IsNullOrEmpty()).ThrowIfTrue("请传入要结算的内容!");
if (Items.IsNullOrEmpty()) Items = new List<Settlement>();
if (Item != null) Items.Add(Item);
Items.Exists(o => o.PayerName.IsNullOrEmpty()).ThrowIfTrue("付款单位不能为空!");
Items.Exists(o => o.Currency == CurrencyUnit.Unsupported).ThrowIfTrue("不支持的货币单位!");
Items.Exists(o => o.Details.IsNullOrEmpty()).ThrowIfTrue("结算明细不能为空!");
var connF = KiviiContext.GetOpenedDbConnection<Settlement>();
if (connF.Exists<Settlement>(o => o.OffsetKvid == Guid.Empty && Sql.In(o.BizKvid, Items.ConvertAll(p => p.BizKvid)))) throw new Exception("存在重复结算的对象!");
var connE = KiviiContext.GetOpenedDbConnection<G>();
//如果泛型类型不是Settlement本身就需要判断
if (typeof(G) != typeof(Settlement)) connE.Exists<EntitySettlement<G>>(o => o.OffsetKvid == Guid.Empty && Sql.In(o.BizKvid, Items.ConvertAll(p => p.BizKvid))).ThrowIfTrue("存在重复结算的对象!");
var allDetails = new List<SettlementDetail>();
Items.ForEach(o => allDetails.AddRange(o.Details));
if (connE.Exists<EntitySettlementDetail<G>>(o => o.OffsetKvid != Guid.Empty && Sql.In(o.BizKvid, allDetails.ConvertAll(p => p.BizKvid)))) throw new Exception("存在重复结算的明细项目!");
var rtns = new RestfulCreateResponse<Settlement>();
rtns.Results = new List<Settlement>();
#region 合并数据库连接,并启用事务
IDbTransaction tranE = null, tranF = null;
if (connE.ConnectionString == connF.ConnectionString)
{
connF.Close();
connF = connE;
tranE = connE.OpenTransaction();
}
else
{
tranE = connE.OpenTransaction();
tranF = connF.OpenTransaction();
}
#endregion
try
{
foreach (var item in Items)
{
if (item.Amount != item.Details.Sum(o => o.Amount) || item.AmountPlan != item.Details.Sum(o => o.AmountPlan)) throw new Exception("项目明细总费用和结算主体总费用不一致!");
var settlementKvid = Guid.NewGuid();
#region 生成财务 Settlement 主结算
var settlement = new Settlement();
settlement.PopulateWith(item);
settlement.Kvid = settlementKvid;
settlement.OwnerKvid = KiviiContext.CurrentMember.DepartmentKvid;
settlement.OwnerName = KiviiContext.CurrentMember.DepartmentName;
settlement.BizId = item.BizId;
settlement.BizKvid = item.BizKvid;
settlement.BizType = typeof(G).FullName;
settlement.OperateTime = item.OperateTime == DateTime.MinValue ? DateTime.Now : item.OperateTime;
settlement.OperatorName = KiviiContext.CurrentMember.FullName;
settlement.OperatorKvid = KiviiContext.CurrentMember.Kvid;
connF.Insert(settlement);
rtns.Results.Add(settlement);
if (typeof(G) != typeof(Settlement))
{
var entitySettlement = new EntitySettlement<G>();
entitySettlement.PopulateInstance(settlement);
connE.Insert(entitySettlement);
}
#endregion
#region 回写更新G数据
var instance = Activator.CreateInstance(typeof(G)) as IEntityHasSettlement;
if (instance != null)
{
instance.Kvid = item.BizKvid;
instance.AmountSettlement = settlement.Amount;
instance.AddOnlyProperties(o => o.AmountSettlement);
connE.UpdateOnly(instance);
}
#endregion
#region 生成泛型SettlementDetail
foreach (var detail in item.Details)
{
detail.SettlementKvid = settlementKvid;
detail.Quantity = detail.Quantity <= 0 ? 1 : detail.Quantity;
detail.QuantityUnit = detail.QuantityUnit.IsNullOrEmpty() ? "次" : detail.QuantityUnit;
detail.QuantityUnitPrice = detail.Amount / detail.Quantity;
detail.Currency = item.Currency;
detail.OperateTime = DateTime.Now;
detail.OperatorKvid = KiviiContext.CurrentMember.Kvid;
detail.OperatorName = KiviiContext.CurrentMember.FullName;
var entityDetail = new EntitySettlementDetail<G>();
entityDetail.PopulateInstance(detail);
connE.Insert(entityDetail);
}
#endregion
}
tranE?.Commit();
tranF?.Commit();
}
catch (Exception ex)
{
tranE?.Rollback();
tranF?.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "结算读取")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementRead : RestfulExecutionGeneric<Settlement>
{
public Guid Kvid { get; set; }
public override object OnExecutionGeneric<G>(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<EntitySettlement<G>>();
var rtns = new RestfulReadResponse<EntitySettlement<G>>();
rtns.Result = conn.SingleById<EntitySettlement<G>>(Kvid);
return rtns;
}
}
[Api(Description = "结算查询")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementQuery : RestfulQuery<Settlement>
{
}
[Api(Description = "结算删除")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementDelete : RestfulExecutionGeneric<Settlement>
{
public List<Guid> Kvids { get; set; }
public override object OnExecutionGeneric<G>(IRequest req, IResponse res)
{
Kvids.ThrowIfNullOrEmpty("参数错误:未传入要删除的结算Kvids");
var connF = KiviiContext.GetOpenedDbConnection<Settlement>();
var connE = KiviiContext.GetOpenedDbConnection<EntitySettlement<G>>();
connF.Exists<Settlement>(o => o.PayKvid != Guid.Empty && Sql.In(o.Kvid, Kvids)).ThrowIfTrue("删除错误:删除的结算中包含已收款的结算!");
//如果泛型类型不是Settlement本身就需要判断
if (typeof(G) != typeof(Settlement)) connE.Exists<EntitySettlement<G>>(o => o.PayKvid != Guid.Empty && Sql.In(o.Kvid, Kvids)).ThrowIfTrue("删除错误:删除的结算中包含已收款的结算!");
var settlements = connF.Select<Settlement>(o => Sql.In(o.Kvid, Kvids));
//var entitySettlements = connE.Select<EntitySettlement<G>>(o => Sql.In(o.Kvid, Kvids));
//var entitySettlementDetails = connE.Select<EntitySettlementDetail<G>>(o => Sql.In(o.SettlementKvid, Kvids));
var billDetails = connF.Select<BillDetail>(o => Sql.In(o.BizKvid, Kvids));
var offsetBillDetails = new List<BillDetail>();
foreach (var item in billDetails)
{
offsetBillDetails.Add(item.Offset("结算项已删除"));
}
var queryBillKvids = connF.From<BillDetail>();
queryBillKvids.SelectDistinct(o => o.BillKvid);
queryBillKvids.Where(o => Sql.In(o.BizKvid, Kvids));
//查出所有的Bill Kvid集合
var billKvids = connF.Column<Guid>(queryBillKvids);
var rtns = new RestfulDeleteResponse<Settlement>();
rtns.Results = new List<Guid>();
#region 合并数据库连接,并启用事务
IDbTransaction tranE = null, tranF = null;
if (connE.ConnectionString == connF.ConnectionString)
{
connF.Close();
connF = connE;
tranE = connE.OpenTransaction();
}
else
{
tranE = connE.OpenTransaction();
tranF = connF.OpenTransaction();
}
#endregion
try
{
//删除Settlement的数据
var updateSettlements = connF.From<Settlement>();
updateSettlements = updateSettlements.Update(o => o.Status);
updateSettlements = updateSettlements.Where(o => Sql.In(o.Kvid, Kvids));
connF.UpdateOnly<Settlement>(new Settlement { Status = -1 }, updateSettlements);
//删除泛型Settlement的数据
if (typeof(G) != typeof(Settlement))
{
var updateEntitySettlements = connE.From<EntitySettlement<G>>();
updateEntitySettlements = updateEntitySettlements.Update(o => o.Status);
updateEntitySettlements = updateEntitySettlements.Where(o => Sql.In(o.Kvid, Kvids));
connE.UpdateOnly<EntitySettlement<G>>(new EntitySettlement<G> { Status = -1 }, updateEntitySettlements);
#region 回写更新G数据
var instance = Activator.CreateInstance(typeof(G)) as IEntityHasSettlement;
if (instance != null)
{
foreach (var item in settlements)
{
instance.Kvid = item.BizKvid;
instance.AmountSettlement = 0;
instance.AddOnlyProperties(o => o.AmountSettlement);
connE.UpdateOnly(instance);
}
}
#endregion
}
//删除泛型SettlementDetail的数据
var updateEntitySettlementDetails = connE.From<EntitySettlementDetail<G>>();
updateEntitySettlementDetails = updateEntitySettlementDetails.Update(o => o.Status);
updateEntitySettlementDetails = updateEntitySettlementDetails.Where(o => Sql.In(o.SettlementKvid, Kvids));
connE.UpdateOnly<EntitySettlementDetail<G>>(new EntitySettlementDetail<G> { Status = -1 }, updateEntitySettlementDetails);
//作废账单中包含当前结算项的明细
#region 作废账单中包含当前结算项的明细
foreach (var item in billDetails)
{
connF.UpdateOnly(item);
}
foreach (var item in offsetBillDetails)
{
connF.Insert(item);
}
if (!billKvids.IsNullOrEmpty())
{
var updateBills = connE.From<Bill>();
updateBills = updateBills.Update(o => o.Summary);
updateBills = updateBills.Where(o => Sql.In(o.Kvid, billKvids));
connE.UpdateOnly<Bill>(new Bill { Summary = "账单内容发生变动,请核查!" }, updateBills);
}
#endregion
rtns.Results.AddRange(Kvids);
tranF?.Commit();
tranE?.Commit();
}
catch (Exception ex)
{
tranF?.Rollback();
tranE?.Rollback();
throw ex;
}
return rtns;
}
}
[Api(Description = "泛型结算明细Query")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementDetailGenericQuery : RestfulExecutionGeneric<SettlementDetail>
{
#region QueryArgs
public virtual int? Skip { get; set; }
public virtual int? Take { get; set; }
public virtual string OrderBy { get; set; }
public string OrderByDesc { get; set; }
public virtual string Include { get; set; }
public virtual string Fields { get; set; }
public string QueryKeys { get; set; }
public string QueryValues { get; set; }
#endregion
public override object OnExecutionGeneric<G>(IRequest req, IResponse res)
{
var conn = KiviiContext.GetOpenedDbConnection<EntitySettlementDetail<G>>();
var dynamicParams = Request.GetRequestParams();
var autoQuery = Request.TryResolve<IAutoQueryDb>();
autoQuery.IncludeTotal = true;
var request = new RestfulQuery<EntitySettlementDetail<G>>();
request = request.PopulateWith(this);
var sqlExpress = autoQuery.CreateQuery(Request, conn, request, dynamicParams);
var rtns = autoQuery.Execute(Request, conn, request, sqlExpress);
return rtns;
}
}
[Api(Description = "结算调价")]
[RequiresAnyRole(SystemRoles.Everyone)]
public class SettlementModifyAmount : RestfulExecutionGeneric<Settlement>
{
public List<Guid> Kvids { get; set; }
public decimal Amount { get; set; }
public string Remark { get; set; }
public override object OnExecutionGeneric<G>(IRequest req, IResponse res)
{
Kvids.ThrowIfNullOrEmpty("参数错误:未传入要调价的结算Kvids");
(Amount < 0).ThrowIfTrue("调整总价格不能为负值!");
var connFins = KiviiContext.GetOpenedDbConnection<Settlement>();
connFins.Exists<Settlement>(o => (o.PayKvid != Guid.Empty || o.OffsetKvid != Guid.Empty) && Sql.In(o.Kvid, Kvids)).ThrowIfTrue("调价错误:存在已支付或待支付或已冲销的结算!");
var connG = KiviiContext.GetOpenedDbConnection<EntitySettlement<G>>();
//如果泛型类型不是Settlement本身就需要判断
if (typeof(G) != typeof(Settlement)) connG.Exists<EntitySettlement<G>>(o => (o.PayKvid != Guid.Empty || o.OffsetKvid != Guid.Empty) && Sql.In(o.Kvid, Kvids)).ThrowIfTrue("调价错误:存在已支付或待支付或已冲销的结算!");
var querySettlements = connFins.From<Settlement>();
querySettlements.Where(o => Sql.In(o.Kvid, Kvids));
querySettlements.OrderBy(o => o.Amount);
var settlements = connFins.Select(querySettlements);
settlements.ThrowIfNullOrEmpty("未找到要调价的结算单!");
(settlements.Count != Kvids.Count).ThrowIfTrue("所选结算单信息不完整!");
settlements.Exists(o => o.PayKvid != Guid.Empty || o.OffsetKvid != Guid.Empty).ThrowIfTrue("调价错误:存在已支付或待支付或已冲销的结算!");
var amountSum = settlements.Sum(o => o.Amount);
var rates = new Dictionary<Guid, decimal>();
foreach (var item in settlements)
{
var rate = Math.Round(item.Amount / amountSum, 2);
rates.Add(item.Kvid, rate);
}
if (rates.ContainsKey(Guid.Empty)) throw new Exception("存在Kvid为空值!");
//差值
var deviation = 1 - rates.Sum(o => o.Value);
if (deviation > 0)
{
var first = rates.FirstOrDefault();
rates[first.Key] += deviation;
}
else if (deviation < 0)
{
foreach (var rate in rates)
{
if (rate.Value + deviation <= 0) continue;
//{
// rates[rate.Key] += deviation * rate.Value;
// deviation -= deviation * rate.Value;
//}
else rates[rate.Key] += deviation;
}
}
if (rates.Sum(o => o.Value) != 1) throw new Exception($"总比和:{rates.Sum(o => o.Value)},比例错误!");
var amountDeviation = Amount - amountSum;
foreach (var item in settlements)
{
decimal rate = 0;
if (!rates.TryGetValue(item.Kvid, out rate)) throw new Exception($"{item.BizId},{item.Kvid}");
var amount = rate * amountDeviation;
item.Amount += amount;
item.AddOnlyProperties(o => o.Amount);
}
if (settlements.Sum(o => o.Amount) != Amount) throw new Exception($"批量调价总额出错:{settlements.Sum(o => o.Amount)}");
var rtns = new RestfulQueryResponse<Settlement>();
rtns.Results = new List<Settlement>();
#region 合并数据库连接,并启用事务
IDbTransaction tranG = null, tranFins = null;
if (connG.ConnectionString == connFins.ConnectionString)
{
connFins.Close();
connFins = connG;
tranG = connG.OpenTransaction();
}
else
{
tranG = connG.OpenTransaction();
tranFins = connFins.OpenTransaction();
}
#endregion
try
{
foreach (var item in settlements)
{
connFins.UpdateOnly(item);
rtns.Results.Add(item);
decimal rate = 0;
if (!rates.TryGetValue(item.Kvid, out rate)) throw new Exception($"{item.BizId},{item.Kvid}");
var amount = rate * amountDeviation;
//如果泛型类型不是Settlement本身就需要判断
if (typeof(G) != typeof(Settlement))
{
var entitySettlement = new EntitySettlement<G>();
entitySettlement.PopulateWith(item);
entitySettlement.Kvid = item.Kvid;
entitySettlement.Amount = item.Amount;
entitySettlement.AddOnlyProperties(o => o.Amount);
connG.UpdateOnly(entitySettlement);
}
#region 新增结算明细
var detail = new EntitySettlementDetail<G>();
detail.SettlementKvid = item.Kvid;
detail.OperateTime = DateTime.Now;
detail.OperatorName = KiviiContext.CurrentMember.FullName;
detail.OperatorKvid = KiviiContext.CurrentMember.Kvid;
detail.BizId = item.BizId;
detail.BizKvid = item.BizKvid;
detail.BizType = item.BizType;
detail.Amount = amount;
detail.AmountPlan = 0;
detail.GoodsFullName = "调价";
detail.GoodsUnit = "次";
detail.Quantity = 1;
detail.QuantityUnitPrice = detail.Amount / detail.Quantity;
detail.QuantityPlan = detail.Quantity;
detail.QuantityUnitPricePlan = detail.AmountPlan / detail.QuantityPlan;
detail.QuantityUnit = "次";
detail.Type = "Service";
detail.Currency = item.Currency;
detail.Remark = $"业务调价原因:{Remark},费用变更:{amount}元,操作人:{KiviiContext.CurrentMember.FullName}";
connG.Insert(detail);
#endregion
#region 回写更新G数据
var instance = Activator.CreateInstance(typeof(G)) as IEntityHasSettlement;
if (instance != null)
{
instance.Kvid = item.BizKvid;
instance.AmountSettlement = item.Amount;
instance.AddOnlyProperties(o => o.AmountSettlement);
connG.UpdateOnly(instance);
}
#endregion
}
tranFins?.Commit();
tranG?.Commit();
}
catch (Exception ex)
{
tranFins?.Rollback();
tranG?.Rollback();
throw ex;
}
rtns.Total = rtns.Results.Count();
return rtns;
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Kivii.Common" version="5.6.2021.10000" targetFramework="net45" />
<package id="Kivii.Core" version="5.6.2021.10000" targetFramework="net45" />
<package id="Kivii.Linq" version="5.6.2021.10190" targetFramework="net45" />
</packages>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment