public void Journal_AddXact_DoesNotAllowToAddTwoXactsWithTheSameUUIDButDifferentPostAmounts() { Account account1 = new Account(); Account account2 = new Account(); Journal journal = new Journal(); Xact xact1 = new Xact(); xact1.SetTag("UUID", Value.StringValue("val1")); xact1.AddPost(new Post() { Account = account1, Amount = new Amount(10) }); xact1.AddPost(new Post() { Account = account2, Amount = new Amount(-10) }); Xact xact2 = new Xact(); xact2.SetTag("UUID", Value.StringValue("val1")); xact2.AddPost(new Post() { Account = account1, Amount = new Amount(5) }); xact2.AddPost(new Post() { Account = account2, Amount = new Amount(-5) }); journal.AddXact(xact1); Assert.Throws <RuntimeError>(() => journal.AddXact(xact2)); }
public void Journal_AddXact_AllowsToAddDifferentUUID() { Account account1 = new Account(); Account account2 = new Account(); Journal journal = new Journal(); Xact xact1 = new Xact(); xact1.SetTag("UUID", Value.StringValue("val1")); xact1.AddPost(new Post() { Account = account1, Amount = new Amount(10) }); xact1.AddPost(new Post() { Account = account2, Amount = new Amount(-10) }); Xact xact2 = new Xact(); xact2.SetTag("UUID", Value.StringValue("val2")); xact2.AddPost(new Post() { Account = account1, Amount = new Amount(10) }); xact2.AddPost(new Post() { Account = account2, Amount = new Amount(-10) }); journal.AddXact(xact1); journal.AddXact(xact2); Assert.Equal(xact1, journal.ChecksumMapping["val1"]); Assert.Equal(xact2, journal.ChecksumMapping["val2"]); }
public void Journal_AddXact_DoesNotAllowToAddTwoXactsWithTheSameUUIDButDifferentNumberOfPosts() { Account account = new Account(); Journal journal = new Journal(); Xact xact1 = new Xact(); xact1.SetTag("UUID", Value.StringValue("val1")); xact1.AddPost(new Post() { Account = account, Amount = new Amount(10) }); xact1.AddPost(new Post() { Account = account, Amount = new Amount(-10) }); Xact xact2 = new Xact(); xact2.SetTag("UUID", Value.StringValue("val1")); xact2.AddPost(new Post() { Account = account, Amount = new Amount(10) }); xact2.AddPost(new Post() { Account = account, Amount = new Amount(-5) }); xact2.AddPost(new Post() { Account = account, Amount = new Amount(-5) }); journal.AddXact(xact1); journal.AddXact(xact2); }
public Xact ReadXact(bool richData) { string line = NextLine(Context.Reader); string origLine = line; if (String.IsNullOrEmpty(line) || !Index.Any()) { return(null); } Context.LineNum++; Xact xact = new Xact(); Post post = new Post(); xact.State = ItemStateEnum.Cleared; xact.Pos = new ItemPosition() { PathName = Context.PathName, BegPos = (int)Context.Reader.Position, BegLine = Context.LineNum, Sequence = Context.Sequence++ }; post.Xact = xact; post.Pos = new ItemPosition() { PathName = Context.PathName, BegPos = (int)Context.Reader.Position, BegLine = Context.LineNum, Sequence = Context.Sequence++ }; post.State = ItemStateEnum.Cleared; post.Account = null; int n = 0; Amount amt = null; string total = null; string field; while (!String.IsNullOrEmpty(line) && n < Index.Count) { field = ReadField(ref line); switch (Index[n]) { case CsvHeadersEnum.FIELD_DATE: xact.Date = TimesCommon.Current.ParseDate(field); break; case CsvHeadersEnum.FIELD_DATE_AUX: if (!String.IsNullOrEmpty(field)) { xact.DateAux = TimesCommon.Current.ParseDate(field); } break; case CsvHeadersEnum.FIELD_CODE: if (!String.IsNullOrEmpty(field)) { xact.Code = field; } break; case CsvHeadersEnum.FIELD_PAYEE: { bool found = false; foreach (Tuple <Mask, string> value in Context.Journal.PayeeAliasMappings) { Logger.Current.Debug("csv.mappings", () => String.Format("Looking for payee mapping: {0}", value.Item1)); if (value.Item1.Match(field)) { xact.Payee = value.Item2; found = true; break; } } if (!found) { xact.Payee = field; } break; } case CsvHeadersEnum.FIELD_AMOUNT: { amt = new Amount(); amt.Parse(ref field, AmountParseFlagsEnum.PARSE_NO_REDUCE); if (!amt.HasCommodity && CommodityPool.Current.DefaultCommodity != null) { amt.SetCommodity(CommodityPool.Current.DefaultCommodity); } post.Amount = amt; break; } case CsvHeadersEnum.FIELD_COST: { amt = new Amount(); amt.Parse(ref field, AmountParseFlagsEnum.PARSE_NO_REDUCE); if (!amt.HasCommodity && CommodityPool.Current.DefaultCommodity != null) { amt.SetCommodity(CommodityPool.Current.DefaultCommodity); } post.Cost = amt; break; } case CsvHeadersEnum.FIELD_TOTAL: total = field; break; case CsvHeadersEnum.FIELD_NOTE: if (!String.IsNullOrEmpty(field)) { xact.Note = field; } break; case CsvHeadersEnum.FIELD_UNKNOWN: if (!String.IsNullOrEmpty(Names[n]) && !String.IsNullOrEmpty(field)) { xact.SetTag(Names[n], Value.StringValue(field)); } break; default: throw new InvalidOperationException(); } n++; } if (richData) { xact.SetTag("Imported", Value.StringValue(TimesCommon.Current.FormatDate(TimesCommon.Current.CurrentDate, FormatTypeEnum.FMT_WRITTEN).ToString())); xact.SetTag("CSV", Value.StringValue(origLine)); } // Translate the account name, if we have enough information to do so foreach (Tuple <Mask, Account> value in Context.Journal.PayeesForUnknownAccounts) { if (value.Item1.Match(xact.Payee)) { post.Account = value.Item2; break; } } xact.AddPost(post); // Create the "balancing post", which refers to the account for this data post = new Post(); post.Xact = xact; post.Pos = new ItemPosition() { PathName = Context.PathName, BegPos = (int)Context.Reader.Position, BegLine = Context.LineNum, Sequence = Context.Sequence++ }; post.State = ItemStateEnum.Cleared; post.Account = Context.Master; if (amt != null && !amt.IsEmpty) { post.Amount = amt.Negated(); } if (!String.IsNullOrEmpty(total)) { amt = new Amount(); amt.Parse(ref total, AmountParseFlagsEnum.PARSE_NO_REDUCE); if (!amt.HasCommodity && CommodityPool.Current.DefaultCommodity != null) { amt.SetCommodity(CommodityPool.Current.DefaultCommodity); } post.AssignedAmount = amt; } xact.AddPost(post); return(xact); }