/// <summary> /// Post a matched transaction. /// May be called direct from StatementMatch for Same transactions, /// or when the user presses "Save" for other transactions /// </summary> public AjaxReturn StatementMatchPost(JObject json) { Utils.Check(SessionData.StatementMatch != null, "Invalid call to StatementMatchPost"); MatchInfo match = SessionData.StatementMatch.ToObject<MatchInfo>(); JArray transactions = SessionData.StatementImport.transactions; dynamic transaction = match.transaction < 0 ? null : SessionData.StatementImport.transactions[match.transaction]; DocType type = match.transaction < 0 ? match.type == "Transfer" ? DocType.Transfer : DocType.Cheque : (DocType)((JObject)transactions[match.transaction]).AsInt("DocumentTypeId"); AjaxReturn result; switch (type) { case DocType.Payment: result = new Customer() { Context = Context, GetParameters = GetParameters, PostParameters = PostParameters, Parameters = Parameters, }.PaymentPost(json.To<CustomerSupplier.PaymentDocument>()); break; case DocType.BillPayment: result = new Supplier() { Context = Context, GetParameters = GetParameters, PostParameters = PostParameters, Parameters = Parameters, }.PaymentPost(json.To<CustomerSupplier.PaymentDocument>()); break; case DocType.Cheque: case DocType.Deposit: case DocType.CreditCardCharge: case DocType.CreditCardCredit: result = DocumentPost(json.To<BankingDocument>()); break; case DocType.Transfer: result = TransferPost(json.To<TransferDocument>()); break; default: throw new CheckException("Unexpected document type:{0}", type.UnCamel()); } if (result.error == null) { if(match.transaction >= 0 && match.type == "Same") transaction.Matched = 1; JArray items = SessionData.StatementImport.import; items.RemoveAt(match.current); result.redirect = "/banking/" + (items.Count == 0 ? "detail" : "statementmatching") + ".html?id=" + match.id; } return result; }
/// <summary> /// Update a matched transaction /// </summary> public void StatementMatch() { Utils.Check(SessionData.StatementMatch != null, "Invalid call to StatementMatch"); MatchInfo match = SessionData.StatementMatch.ToObject<MatchInfo>(); Account account = Database.Get<Account>(match.id); // The existing transaction to match (or empty record if none) Extended_Document transaction = match.transaction < 0 ? Database.EmptyRecord<Extended_Document>() : SessionData.StatementImport.transactions[match.transaction].ToObject<Extended_Document>(); // The statement transaction dynamic current = SessionData.StatementImport.import[match.current]; Utils.Check(current != null, "No current transaction"); bool same = match.type == "Same"; bool documentHasVat = false; bool payment = false; decimal cAmount = current.Amount; int id = transaction.idDocument ?? 0; DocType type = match.transaction < 0 ? match.type == "Transfer" ? DocType.Transfer : // They specified a new transfer cAmount < 0 ? // They specified a new transaction - type depends on sign and account type account.AccountTypeId == (int)AcctType.Bank ? DocType.Cheque : DocType.CreditCardCharge : account.AccountTypeId == (int)AcctType.Bank ? DocType.Deposit : DocType.CreditCardCredit : (DocType)transaction.DocumentTypeId; GetParameters["acct"] = match.id.ToString(); // This bank account // Call appropriate method to get Record, and therefore transaction // Also set Module and Method, so appropriate template is used to display transaction before posting switch (type) { case DocType.Payment: Module = "customer"; Method = "payment"; Customer cust = new Customer(); cust.Payment(id); this.Record = cust.Record; payment = true; break; case DocType.BillPayment: Module = "supplier"; Method = "payment"; Supplier supp = new Supplier(); supp.Payment(id); this.Record = supp.Record; payment = true; break; case DocType.Cheque: case DocType.Deposit: case DocType.CreditCardCharge: case DocType.CreditCardCredit: Method = "document"; Document(id, type); documentHasVat = true; break; case DocType.Transfer: Method = "transfer"; Transfer(id); break; default: throw new CheckException("Unexpected document type:{0}", type.UnCamel()); } dynamic record = (JObject)Record; dynamic doc = record.header; if (id == 0 && type == DocType.Transfer && cAmount > 0) { // New transfer in doc.TransferAccountId = match.id; doc.DocumentAccountId = 0; doc.DocumentAccountName = ""; } if (string.IsNullOrWhiteSpace(doc.DocumentMemo.ToString())) { // Generate a memo string name = current.Name; string memo = current.Memo; if (string.IsNullOrWhiteSpace(memo)) memo = name; else if (!memo.Contains(name)) memo = name + " " + memo; doc.DocumentMemo = memo; } if (!same) { // They want to create a new document - try to guess the DocumentName string name = doc.DocumentName; string currentName = current.Name; currentName = currentName.Split('\n', '\t')[0]; if (string.IsNullOrWhiteSpace(name) || (!payment && name.SimilarTo(currentName) < 0.5)) { doc.DocumentName = currentName; NameAddress n = new NameAddress() { Type = "O", Name = currentName }; doc.DocumentNameAddressId = Database.Get(n).idNameAddress ?? 0; } } doc.DocumentDate = current.Date; decimal tAmount = doc.DocumentAmount; decimal diff = Math.Abs(cAmount) - Math.Abs(tAmount); doc.DocumentAmount += diff; if(same) Utils.Check(diff == 0, "Amounts must be the same"); else { // New transaction doc.DocumentOutstanding = doc.DocumentAmount; doc.Clr = ""; doc.idDocument = doc.Id = null; if (Utils.ExtractNumber(doc.DocumentIdentifier.ToString()) > 0) doc.DocumentIdentifier = ""; } if(string.IsNullOrEmpty(doc.DocumentIdentifier.ToString())) { if (current.Id != null) { doc.DocumentIdentifier = current.Id; } else { int no = Utils.ExtractNumber(current.Name.ToString()); if (no != 0) doc.DocumentIdentifier = no.ToString(); } } if (diff != 0 && documentHasVat) { // Adjust first line to account for difference if (record.detail.Count == 0) record.detail.Add(new InvoiceLine().ToJToken()); dynamic line = record.detail[0]; decimal val = line.LineAmount + line.VatAmount + diff; if (line.VatRate != 0) { line.VatAmount = Math.Round(val * line.VatRate / (100 + line.VatRate), 2); val -= line.VatAmount; } line.LineAmount = val; } if (payment && !same) removePayments(record); record.StatementAccount = match.id; if (same) { // Just post the new information if (type == DocType.Transfer) record = record.header; // Transfer posts header alone AjaxReturn p = StatementMatchPost((JObject)record); if (p.error == null) Redirect(p.redirect); // If no error, go on with matching } }