Skip to main content
Technical ManualLooking for step-by-step instructions? In the User Manual: BillsVendor Payments and 1099 Totals

Purchases

The Purchases module keeps vendor bills, payments to vendors, what the business owes each vendor and the 1099 totals. It replaces the legacy purchases, operating expenses and other expenses screens (BOOK-09 to BOOK-12, whose mixed bills were three tables tied by a ParentId) and My Credit Payments (BOOK-08).

ProjectsCrestApps.Almohaseb.Purchases (module), CrestApps.Almohaseb.Purchases.Core (documents, index, rules, service, posting rules)
FeatureCrestApps.Almohaseb.Purchases (Purchases & payables), enabled by both business recipes
CollectionPurchases

Features and dependencies​

CrestApps.Almohaseb.Purchases depends on CrestApps.Almohaseb.Ledger, CrestApps.Almohaseb.Contacts and CrestApps.Almohaseb.Items. The core reads vendors and purchase categories through IVendorLookup and IPurchaseCategoryLookup, implemented in the module over the content items (ContentVendorLookup lists the active vendors of categories whose usage includes purchases, operating or other expenses; ContentPurchaseCategoryLookup the purchase categories and retail categories used on purchases).

Documents​

DocumentFields
BillVendorId, Date, PaymentMethodId, InvoiceNumber, Description, AskAccountant, Lines (BillLine: CategoryId or AccountId, Amount, RelatedMonth, Memo), and, set by the service, Total, OnAccount (the method's role is OnAccount) and PaidFor1099
VendorPaymentVendorId, Date, Amount, PaymentMethodId, CheckNumber, Comment, PaidFor1099

Both implement IPayableDocument (Revision, ModifiedUtc, ModifiedBy) and are posted again at every save. PayableIndex (ItemId, VendorId, Date, Kind, Total, Owed, Paid, InvoiceNumber) is filled by BillIndexProvider and VendorPaymentIndexProvider: a bill on account has Owed = Total, a payment Owed = -Amount; Paid is what counts for the 1099. Migration version 1 creates it (PurchasesMigrations.CreatePayableIndexAsync) with database indexes on VendorId, Date, Kind, Date and ItemId.

Posting​

Rule (source type)Entry
BillPostingRule (Purchases.Bill)Each line: Dr the purchase category's account (only a purchase-kind category's own account; a retail category used on purchases posts to the system Purchases account) or the expense account; the total: Cr PaymentMethodAccounts.Resolve(method, Paid) — Accounts Payable when on account, Undeposited Funds by default. Negative amounts take the other side.
VendorPaymentPostingRule (Purchases.VendorPayment)Dr Accounts Payable; Cr the payment method's account.

Rules​

IPayablesService / PayablesService validates (PayableValidationCode):

  • an existing vendor and a date; an existing payment method;
  • a bill has lines; each line has exactly one of a purchase category and an account (LineTargetRequired); the category must be a purchase category and the account an active cost, expense, other expense or non-system asset account (LineTargetNotFound); at most two decimals; the total is not zero;
  • a new bill with the same vendor, date and total as another is refused with PossibleDuplicate unless allowDuplicate (the legacy duplicate warning);
  • a payment is positive, not OnAccount, and not more than what is owed without it (MoreThanOwed).

The document is saved only when its posting succeeds; DeleteAsync unposts and then deletes. GetBalanceAsync, ListBalancesAsync and Get1099TotalsAsync(year) are one GROUP BY VendorId over PayableIndex in the session's transaction (replacing the legacy GetVendorTotalCredit stored procedure). GetUsualPaymentMethodAsync(vendor) returns the most used method of the vendor's last five bills (the legacy smart default).

1099​

The totals are the Form1099TotalsReport (almohaseb-1099-totals, category Taxes, ViewAlmohasebPurchases) of CrestApps Reports, registered by Form1099ReportsStartup with [RequireFeatures("CrestApps.OrchardCore.Reports")], for the calendar year of the end of the range.

PaidFor1099 is the amount, except for card payments (reported on a 1099-K by the processor) and bills on account (counted when paid). Get1099TotalsAsync keeps the vendors marked Is1099, each with its Box (Vendor1099Total.Box, Threshold, MustFile): the vendor's VendorPart.Form1099Box in the year's content, else NEC-1, else the first box. Without the year's content Box is null and nothing is required.

The boxes are tax content: Form1099Content (tax-us-1099-{year}: Year, Verified, VerifiedBy, Source, Boxes: Form1099Box Code such as NEC-1 or MISC-1, Form, Box, Name, Threshold; Find(code)), kept by IForm1099ContentStore / Form1099ContentStore (GetAsync, GetLatestAsync(year), ListAsync, SaveAsync (replaces the year, unverified), VerifyAsync) with Form1099ContentIndex (Year, migration 2). The AlmohasebForm1099Content recipe step loads { "name": "AlmohasebForm1099Content", "Years": [ { "Year": 2026, "Source": "…", "Boxes": [ … ] } ] }; both business recipes load 2025 ($600) and 2026 ($2,000 for NEC-1 and MISC-1, 3 and 6; royalties MISC-2 $10 and attorney proceeds MISC-10 $600), unverified. Vendor1099BoxDisplayDriver adds the 1099 box to the vendor editor (a second driver of AlmohasebVendorPart, shape VendorPart1099Box_Edit, the boxes of the latest year's content).

Permissions​

PermissionKeyDefault roles
Enter bills and pay vendorsManageAlmohasebPurchasesAdministrator
View bills, vendor balances and 1099 totalsViewAlmohasebPurchasesAdministrator
Verify the 1099 tax contentVerifyAlmohaseb1099ContentAdministrator

Admin menu and routes​

A Purchases group (almohasebPurchases) with Bills (almohasebBills), Vendor balances (almohasebVendorBalances) and 1099 boxes (almohaseb1099).

Route (under the admin prefix)Route nameMethods
Almohaseb/Purchases/BillsAlmohasebBillsGET (from, to, vendorId)
Almohaseb/Purchases/Bills/Create, /Edit/{id}, /Save, /Delete/{id}AlmohasebBillCreate, AlmohasebBillEdit, AlmohasebBillSave, AlmohasebBillDeleteGET (vendorId), GET, POST (AllowDuplicate), POST
Almohaseb/Purchases/VendorsAlmohasebVendorBalancesGET
Almohaseb/Purchases/Pay, /Payment/Edit/{id}, /Payment/SaveAlmohasebVendorPaymentCreate, AlmohasebVendorPaymentEdit, AlmohasebVendorPaymentSaveGET (vendorId), GET, POST
Almohaseb/Purchases/1099Almohaseb1099GET (year): the year's 1099 boxes and their verification
Almohaseb/Purchases/1099/VerifyAlmohaseb1099VerifyPOST (year)

The bill editor uses the Admin UI editable grid; its line picker groups the purchase categories (category:{id}) and the accounts (account:{id}).

Extension points​

  • Check printing comes with the banking checks feature; a vendor payment's CheckNumber is where it records the check.
  • Scanned invoices come with the Documents module.

See also​