Esempio n. 1
0
 public override bool Test(JObject data)
 {
     decimal value = data.AsDecimal(JObjectFieldName);
     switch (_comparison) {
         case Comparison.Zero:
             return value == 0;
         case Comparison.NonZero:
             return value != 0;
         case Comparison.Less:
             return value <= _value;
         case Comparison.Greater:
             return value >= _value;
         case Comparison.Equal:
             return value == _value;
         case Comparison.NotEqual:
             return value != _value;
         default:
             return true;
     }
 }
Esempio n. 2
0
 /// <summary>
 /// Remove data from certain tables which repeats in a series of records, and add totals as required
 /// </summary>
 /// <param name="tables">List of tables with potentially repeating data</param>
 /// <returns>Modified list</returns>
 IEnumerable<JObject> removeRepeatsAndTotal(IEnumerable<JObject> data, params string[] tables)
 {
     JObject last = null;
     JObject spacer = new JObject().AddRange("@class", "totalSpacer");
     // All fields in the repeating tables
     HashSet<string> flds = new HashSet<string>(tables.SelectMany(t => Database.TableFor(t).Fields.Select(f => f.Name)));
     // Our fields in the repeating tables
     HashSet<string> fields = new HashSet<string>();
     List<string> essentialFields = _fields.Where(f => f.Essential).Select(f => f.Name).ToList();
     foreach (string f in _fields.Where(f => tables.Contains(f.Table)).Select(f => f.Name))
         fields.Add(f);	// One of our fields in a potentially repeating table (need to be at front)
     foreach (string f in flds.Where(f => !fields.Contains(f)))
         fields.Add(f);	// Rest of potentially repeating fields
     string[] sortFields = _sortFields == null ? new string[0] : _sortFields.Where(f => fieldFor(f).Include).ToArray();
     string[] lastTotalBreak = new string[sortFields.Length + 1];
     Dictionary<string, decimal[]> totals = new Dictionary<string, decimal[]>();
     string firstStringField = null;
     if (_total) {
         // Build list of totalling fields
         foreach (ReportField f in _fields) {
             if (!f.Include) continue;
             string type = f.AsString("type");
             if (firstStringField == null && type == "string" && !sortFields.Contains(f.Name))
                 firstStringField = f.Name;
             if (f.Name == "VatRate") continue;
             if (type != "decimal" && type != "double" && type != "credit" && type != "debit") continue;
             totals[f.Name] = new decimal [sortFields.Length + 1];
         }
     }
     // Function to generate total record - index is sort field number (sortFields.length for grand total)
     Func<int, JObject> totalRecord = delegate(int level) {
         JObject t = new JObject().AddRange("@class", "total");
         foreach (string f in totals.Keys.ToList()) {
             t[f] = totals[f][level];
             totals[f][level] = 0;
         }
         if (firstStringField != null)
             t[firstStringField] = level == sortFields.Length ? "Grand Total" : "Total";
         if(level < sortFields.Length)
             t[sortFields[level]] = lastTotalBreak[level];
         lastTotalBreak[level] = null;
         return t;
     };
     foreach (JObject r in data) {
         JObject record = new JObject(r);
         JObject id = null;
         if (essentialFields.Count > 0) {
             // Make recordId object with essential fields (for user click and expand)
             id = new JObject();
             foreach (string f in essentialFields) {
                 id[f] = record[f];
                 if (!fields.Contains(f))
                     record.Remove(f);		// Don't want essential fields to interfere with checking for duplicates
             }
         }
         if (last != null) {
             if (_total) {
                 for (int level = sortFields.Length; level-- > 0; ) {
                     if (record.AsString(sortFields[level]) != lastTotalBreak[level]) {
                         if (lastTotalBreak[level] != null) {
                             // Output totals for this sort field
                             yield return totalRecord(level);
                             yield return spacer;
                         }
                     }
                 }
             }
             // Now remove duplicate fields
             foreach (string f in fields) {
                 if (last.AsString(f) == record.AsString(f))
                     record.Remove(f);
                 else
                     break;
             }
             if (record.IsAllNull())
                 continue;		// Everything repeats - ignore duplicate record
         }
         if(id != null)
             record["recordId"] = id;
         if (_total) {
             // Cache total break values
             for(int level = 0; level < sortFields.Length; level++)
                 lastTotalBreak[level] = r.AsString(sortFields[level]);
             // Accumulate totals
             foreach (string f in totals.Keys.ToList()) {
                 decimal v = record.AsDecimal(f);
                 if (f == "Credit") {
                     v = r.AsDecimal("Amount");
                     v = v < 0 ? -v : 0;
                 } else if (f == "Debit") {
                     v = r.AsDecimal("Amount");
                     if (v < 0) v = 0;
                 }
                 for(int level = 0; level <= sortFields.Length; level++)
                     totals[f][level] += v;
             }
         }
         last = r;
         yield return record;
     }
     if (_total && last != null) {
         // Print any pending sort field totals
         for (int level = sortFields.Length; level-- > 0; ) {
             if (lastTotalBreak[level] != null) {
                 yield return totalRecord(level);
                 yield return spacer;
             }
         }
         if(_grandTotal)
             yield return totalRecord(sortFields.Length);
     }
 }
Esempio n. 3
0
 IEnumerable<JObject> fixTrialBalance(IEnumerable<JObject> data)
 {
     _total = false;
     JObject last = null;
     decimal retainedProfitOld = 0;
     Dictionary<string, decimal> totals = new Dictionary<string, decimal>();
     // Make list of fields to total
     foreach (ReportField f in _fields) {
         if (!f.Include) continue;
         string type = f.AsString("type");
         if (type != "decimal" && type != "double" && type != "credit" && type != "debit") continue;
         totals[f.Name] = 0;
     }
     // Function to add a total record
     Func<JObject> totalRecord = delegate() {
         JObject t = new JObject().AddRange("@class", "total");
         foreach (string f in totals.Keys.ToList()) {
             t[f] = totals[f];
         }
         t["AccountName"] = "Total";
         return t;
     };
     if (data.FirstOrDefault(r => r.AsInt("idAccount") == (int)Acct.RetainedEarnings) == null) {
         // Must have retained earnings account, so add if missing
         data = data.Concat(Enumerable.Repeat(new JObject().AddRange(
                 "idAccount", (int)Acct.RetainedEarnings,
                 "Heading", "Equities",
                 "BalanceSheet", 1,
                 "AccountTypeId", (int)AcctType.Equity,
                 "AcctType", "Equity",
                 "AccountName", "Retained Earnings",
                 "Negate", 1,
                 "CurrentPeriod", 0M,
                 "PreviousPeriod", 0M,
                 "Old", 0M
         ), 1));
     }
     foreach (JObject r in data) {
         JObject record = new JObject(r);
         if (record.AsInt("BalanceSheet") == 0) {
             // Accumulate all P & L before previous period into retained profit
             retainedProfitOld += record.AsDecimal("Old");
         } else {
             // For balance sheet, add values before previous period into current value
             record["Amount"] = record.AsDecimal("Amount") + record.AsDecimal("Old");
             if (r.AsInt("idAccount") == (int)Acct.RetainedEarnings) {
                 record["Amount"] = record.AsDecimal("Amount") + retainedProfitOld;
                 record.Remove("idAccount");	// So user can't click on it to expand
             }
             record.Remove("Old");
         }
         decimal v = record.AsDecimal("Amount");
         if (v == 0)
             continue;
         // Accumulate totals
         foreach (string f in totals.Keys.ToList()) {
             decimal v1 = v;
             if (f == "Credit") {
                 v1 = v1 < 0 ? -v1 : 0;
             } else if (f == "Debit") {
                 if (v1 < 0) v1 = 0;
             } else {
                 record[f] = v;
             }
             totals[f] += v1;
         }
         last = r;
         yield return record;
     }
     if (last != null) {
         yield return totalRecord();
     }
 }
Esempio n. 4
0
 // TODO: Should be more like fixBalanceSheet, and use common totalling code
 IEnumerable<JObject> fixProfitAndLoss(IEnumerable<JObject> data)
 {
     _total = false;
     JObject last = null;
     string lastTotalBreak = null;
     string lastHeading = null;
     int sign = 1;
     Dictionary<string, decimal[]> totals = new Dictionary<string, decimal[]>();
     JObject spacer = new JObject().AddRange("@class", "totalSpacer");
     // Make list of fields to total
     foreach (ReportField f in _fields) {
         if (!f.Include) continue;
         string type = f.AsString("type");
         if (type != "decimal" && type != "double" && type != "credit" && type != "debit") continue;
         totals[f.Name] = new decimal[3];
     }
     // Function to add a total record (index is total level - 0=account type change, 1=Gross Profit, 2=Net Profit)
     Func<int, JObject> totalRecord = delegate(int index) {
         JObject t = new JObject().AddRange("@class", "total total" + index);
         foreach (string f in totals.Keys.ToList()) {
             t[f] = sign * totals[f][index];
             totals[f][index] = 0;
         }
         t["Heading"] = lastHeading;
         t[_sortOrder] = index == 0 ? "Total " + lastTotalBreak : lastTotalBreak;
         return t;
     };
     foreach (JObject r in data) {
         JObject record = new JObject(r);
         string totalBreak = record.AsString(_sortOrder);
         string heading = record.AsString("Heading");
         if (totalBreak != lastTotalBreak) {
             if (last != null) {
                 if (lastTotalBreak != null) {
                     // Total and spacer for account type change
                     yield return totalRecord(0);
                     yield return spacer;
                     if (lastHeading != heading) {
                         // Total and spacer for gross profit
                         lastTotalBreak = lastHeading;
                         sign = -1;
                         yield return totalRecord(1);
                         yield return spacer;
                     }
                 }
             }
             lastTotalBreak = totalBreak;
             lastHeading = heading;
             // New account type heading
             yield return new JObject().AddRange("@class", "title", "Heading", heading, "AcctType", totalBreak);
         }
         sign = record.AsBool("Negate") ? -1 : 1;
         // Accumulate totals
         foreach (string f in totals.Keys.ToList()) {
             decimal v = record.AsDecimal(f);
             decimal[] tots = totals[f];
             for (int i = 0; i < tots.Length; i++) {
                 tots[i] += v;
             }
             record[f] = sign * v;
         }
         last = r;
         yield return record;
     }
     if (last != null) {
         // Total and spacer for last account type change
         yield return totalRecord(0);
         yield return spacer;
         // Total and spacer for gross profit
         lastTotalBreak = lastHeading;
         sign = -1;
         yield return totalRecord(2);
     }
 }
Esempio n. 5
0
 // TODO: Should use common totalling code
 IEnumerable<JObject> fixBalanceSheet(IEnumerable<JObject> data)
 {
     _total = false;
     JObject last = null;
     string lastTotalBreak = null;
     string lastHeading = null;
     int sign = 1;
     decimal retainedProfitCP = 0, retainedProfitPP = 0, retainedProfitOld = 0;
     Dictionary<string, decimal[]> totals = new Dictionary<string, decimal[]>();
     JObject spacer = new JObject().AddRange("@class", "totalSpacer");
     // Make list of fields to total
     foreach (ReportField f in _fields) {
         if (!f.Include) continue;
         string type = f.AsString("type");
         if (type != "decimal" && type != "double" && type != "credit" && type != "debit") continue;
         totals[f.Name] = new decimal[3];
     }
     // Function to add a total record (index is total level - 0=account type change, 1=heading change, 2=total assets/total liabilities & equity)
     Func<int, JObject> totalRecord = delegate(int index) {
         JObject t = new JObject().AddRange("@class", "total total" + index);
         foreach (string f in totals.Keys.ToList()) {
             t[f] = sign * totals[f][index];
             totals[f][index] = 0;
         }
         if (index == 0) {
             t["Heading"] = lastHeading;
             t[_sortOrder] = "Total " + lastTotalBreak;
         } else {
             t["Heading"] = "Total " + lastHeading;
         }
         return t;
     };
     if(data.FirstOrDefault(r => r.AsInt("idAccount") == (int)Acct.RetainedEarnings) == null) {
         // Need a retained earnings account line, so create one if missing
         data = data.Concat(Enumerable.Repeat(new JObject().AddRange(
                 "idAccount", (int)Acct.RetainedEarnings,
                 "Heading", "Equities",
                 "BalanceSheet", 1,
                 "AccountTypeId", (int)AcctType.Equity,
                 "AcctType", "Equity",
                 "AccountName", "Retained Earnings",
                 "Negate", 1,
                 "CurrentPeriod", 0M,
                 "PreviousPeriod", 0M,
                 "Old", 0M
         ), 1));
     }
     foreach (JObject r in data) {
         JObject record = new JObject(r);
         string totalBreak = record.AsString(_sortOrder);
         string heading = record.AsString("Heading");
         if (record.AsInt("BalanceSheet") == 0) {
             // Accumulate profit and loss postings
             retainedProfitCP += record.AsDecimal("CurrentPeriod");
             retainedProfitPP += record.AsDecimal("PreviousPeriod");
             retainedProfitOld += record.AsDecimal("Old");
             continue;
         } else {
             if (r.AsInt("idAccount") == (int)Acct.RetainedEarnings) {
                 // Add accumulated profit into retained earnings
                 record["PreviousPeriod"] = record.AsDecimal("PreviousPeriod") + retainedProfitOld;
                 record["CurrentPeriod"] = record.AsDecimal("CurrentPeriod") + retainedProfitPP;
                 record.Remove("idAccount");		// So user can't click on it to expand
             }
             // Balance sheet shows totals so far, so add in previous periods
             record["PreviousPeriod"] = record.AsDecimal("PreviousPeriod") + record.AsDecimal("Old");
             record["CurrentPeriod"] = record.AsDecimal("CurrentPeriod") + record.AsDecimal("PreviousPeriod");
             record.Remove("Old");
         }
         if (totalBreak != lastTotalBreak) {
             if (last != null) {
                 if (lastTotalBreak != null) {
                     // Add total and spacer for account type change
                     spacer["Heading"] = lastHeading;
                     yield return totalRecord(0);
                     yield return spacer;
                     if (lastHeading != heading) {
                         // Add total and spacer for heading change
                         lastTotalBreak = lastHeading;
                         yield return totalRecord(1);
                         spacer.Remove("Heading");
                         yield return spacer;
                         if (lastHeading.Contains("Assets") && !heading.Contains("Assets")) {
                             // Add total and spacer for total assets
                             lastHeading = "Assets";
                             sign = 1;
                             yield return totalRecord(2);
                             yield return spacer;
                         }
                     }
                 }
             }
             if (lastHeading != heading)	// Next heading if required
                 yield return new JObject().AddRange("@class", "title", "Heading", heading);
             lastTotalBreak = totalBreak;
             lastHeading = heading;		// Account type heading
             yield return new JObject().AddRange("@class", "title", "Heading", heading, "AcctType", totalBreak);
         }
         sign = record.AsBool("Negate") ? -1 : 1;
         // Accumulate totals
         foreach (string f in totals.Keys.ToList()) {
             decimal v = record.AsDecimal(f);
             decimal[] tots = totals[f];
             for (int i = 0; i < tots.Length; i++) {
                 tots[i] += v;
             }
             record[f] = sign * v;
         }
         last = r;
         // The record itself (now all totals and headings taken care of)
         yield return record;
         if (r.AsInt("idAccount") == (int)Acct.RetainedEarnings) {
             // Generate Net Income posting
             record = new JObject(record);
             record.Remove("idAccount");
             record["AccountName"] = "Net Income";
             record["AccountDescription"] = "";
             record["CurrentPeriod"] = -retainedProfitCP;
             record["PreviousPeriod"] = -retainedProfitPP;
             foreach (string f in totals.Keys.ToList()) {
                 decimal v = record.AsDecimal(f) * sign;
                 decimal[] tots = totals[f];
                 for (int i = 0; i < tots.Length; i++) {
                     tots[i] += v;
                 }
             }
             yield return record;
         }
     }
     if (last != null) {
         // Liabilites and equity total
         yield return totalRecord(0);
         yield return spacer;
         lastHeading = "Liabilities & Equity";
         sign = -1;
         yield return totalRecord(2);
         yield return spacer;
     }
 }
Esempio n. 6
0
 /// <summary>
 /// Update a journal line
 /// </summary>
 public override void Update(JObject dataOut)
 {
     if (dataOut["AccountId"] == null) return;	// Can't post if no account
     int id = dataOut.AsInt("idDocument");
     if (id == 0)
         return;			// Can't post if no document
     bool newTran = id != _tranId;
     DocType docType = (DocType)dataOut.AsInt("DocumentTypeId");
     if (newTran) {
         // New document
         _vat = 0;
         _vatAmount = 0;
         dataOut["DocumentAddress"] = string.Join("\r\n", Enumerable.Range(1, 5).Select(i => dataOut.AsString("Address" + i)).Where(s => !string.IsNullOrEmpty(s)).ToArray());
         if (!_module.Database.RecordExists("Document", dataOut.AsInt("idDocument"))) {
             dataOut["VatPaid"] = 0;
         }
         base.Update(dataOut);		// Save the document
         _tranId = id;
         _line = 0;
         // Save the last invoice/cheque/etc. no
         int number = Utils.ExtractNumber(dataOut.AsString("DocumentIdentifier"));
         switch (docType) {
             case DocType.Invoice:
             case DocType.CreditMemo:
                 if (number > _lastInvoiceNumber) _lastInvoiceNumber = number;
                 break;
             case DocType.Bill:
             case DocType.Credit:
                 if (number > _lastBillNumber) _lastBillNumber = number;
                 break;
             case DocType.Cheque:
             case DocType.CreditCardCharge:
                 registerNumber(_lastChequeNumber, dataOut.AsInt("AccountId"), number);
                 break;
             case DocType.Deposit:
             case DocType.CreditCardCredit:
                 registerNumber(_lastDepositNumber, dataOut.AsInt("AccountId"), number);
                 break;
             case DocType.GeneralJournal:
                 if (number > _lastJournalNumber) _lastJournalNumber = number;
                 break;
         }
         // Delete any existing lines
         _module.Database.Execute("DELETE FROM Line WHERE idLine IN (SELECT idJournal FROM Journal WHERE DocumentId = " + _tranId + ")");
         _module.Database.Execute("DELETE FROM Journal WHERE DocumentId = " + _tranId);
     }
     dataOut["DocumentId"] = _tranId;
     dataOut["JournalNum"] = ++_line;
     dataOut["Memo"] = dataOut["DocumentMemo"];
     _module.Database.Update("Journal", dataOut);	// Save the journal
     if (dataOut.AsInt("AccountId") == (int)Acct.VATControl) {
         // This is the VAT journal
         _vatAmount += dataOut.AsDecimal("Amount");
         if (_vat != 0) {
             // There is already a VAT journal - delete it
             _module.Database.Execute("DELETE FROM Line WHERE idLine IN (SELECT idJournal FROM Journal WHERE DocumentId = " + _tranId + " AND JournalNum = " + _vat + ")");
             _module.Database.Execute("DELETE FROM Journal WHERE DocumentId = " + _tranId + " AND JournalNum = " + _vat);
             _module.Database.Execute("UPDATE Journal SET JournalNum = JournalNum - 1 WHERE DocumentId = " + _tranId + " AND JournalNum > " + _vat);
             // Bump this journal a line earlier
             dataOut["JournalNum"] = --_line;
             dataOut["Amount"] = _vatAmount;
             _module.Database.Update("Journal", dataOut);
         }
         _vat = _line;	// Remember, to avoid 2 VAT lines
     }
     // 2nd and subsequent journals (except VAT journal) have lines
     // NB VAT Payments to HMRC have are exceptions - they have a line for the VAT payment
     if (!newTran && (_line == 2 || dataOut.AsInt("AccountId") != (int)Acct.VATControl)) {
         int sign = AppModule.SignFor(docType);
         dataOut["idLine"] = dataOut["idJournal"];
         dataOut["LineAmount"] = sign * dataOut.AsDecimal("Amount");
         dataOut["Qty"] = sign * dataOut.AsDecimal("Qty");
         dataOut["VatRate"] = dataOut.AsDecimal("VatRate");
         dataOut["VatAmount"] = sign * dataOut.AsDecimal("VatAmount");
         _module.Database.Update("Line", dataOut);
     }
 }