42 Commits

Author SHA1 Message Date
69eae3b7dc Merge branch 'staging' of gitea.story-basking.ts.net:notid/integreat into staging 2026-08-16 08:41:06 -07:00
58f6714399 fix(transactions): drop suppressed transactions from the transactions page (#19)
Suppressed transactions were showing up on the transactions page.

## The cause

The SSR transactions page never excluded `:transaction-approval-status/suppressed`. The GraphQL/CLJS transactions page has always excluded them unconditionally (`auto_ap/datomic/transactions.clj`, the `true` branch of its `cond->>`), as do the ledger queries in `auto_ap/ledger.clj`.

The `scan-transactions` ion does no status filtering of its own — it is a bare `index-range` over `:transaction/client+date` — so nothing downstream was dropping them either. Suppressing a transaction is the user's way of saying "stop showing me this", and it stayed visible on the page it was suppressed from.

## Impact

22,813 suppressed transactions exist db-wide out of 621,492. For the worst-affected client, a five-year range returned **14,736 rows against 3,641 real ones** — the other 11,095 were suppressed.

## The fix

One `not` clause in the main scan in `auto_ap/ssr/transaction/common.clj`, applied inside the query rather than after it so the row count and the amount total — both derived from `fetch-ids` — match the rows actually rendered. Measured 6ms -> 29ms on that worst-case client and range.

The three status routes (approved, unapproved, requires-feedback) constrain the status themselves and there is no suppressed route, so no page loses its own rows.

## Note on the commit list

This branch carries `6e7f66a7` (include-in-reports in the register) and its revert. That commit was only ever on a local staging; it never reached this remote. The net diff of this branch against `staging` is exactly the transactions fix below — the add and the revert cancel out. Both are kept so local staging reconciles cleanly after merge.

## Testing

- New `test/clj/auto_ap/ssr/transaction/common_test.clj` — 6 assertions, passing. Covers the suppressed row being dropped from `ids`, `all-ids` and `count`; the approved status route still listing its rows; and an all-suppressed client returning empty.
- `auto-ap.ssr.transaction.import-test` and `auto-ap.ssr.ledger-test` pass, with one exception: `external-import-remove-button-test` fails identically with and without these changes, so it is pre-existing and unrelated.
- `auto-ap.ssr.transaction.edit-test` still has its known `apply-rule does not exist` compile break, untouched here.
- `lein cljfmt check` clean.

## Loose end, not addressed

`:transaction/exclude-from-ledger` is defined in `resources/schema.edn` but read nowhere in the codebase — only `:invoice/exclude-from-ledger` is honored. If anything ever wrote that attribute expecting it to hide transactions, it never did.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #19
Co-authored-by: Bryce <bryce@brycecovertoperations.com>
Co-committed-by: Bryce <bryce@brycecovertoperations.com>
2026-08-16 08:40:47 -07:00
6e7f66a78f fix(ledger): honor include-in-reports in the register
A journal entry with a line posted to a bank account flagged
:bank-account/include-in-reports false shows on the SSR register but not on
the GraphQL/CLJS ledger page, which has always dropped those entries in
auto-ap.datomic.ledger/graphql-results (and the CSV export does the same in
auto-ap.routes.exports). Same entry, same filters, visible on one page and
missing from the other. The admin client form defaults the flag to false, so
any bank account saved without ticking the box is affected -- 33 of 676 bank
accounts carry an explicit false today.

Resolve the affected entries up front off VAET and drop them before sorting
rather than excluding them inside the query: the equivalent not-join measured
~36ms against ~3ms for the bare scan on a wide date range, while the two
lookups cost ~2ms and are skipped entirely for clients with nothing flagged.

Unlike the GraphQL page, which filters after pagination and so quietly serves
short pages against an unfiltered total, this runs before pagination, so the
row count matches what the register renders.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 18:16:17 -07:00
ab27d3a4da fix(ledger): match bank accounts sharing a code in register account search
A client's bank account and the financial account it posts to share a
numeric code, and :journal-entry-line/account points at either entity.
The register's Account search matched the selected entity id exactly, so
picking a financial account missed every line posted to the bank account
on that code -- while the Account Code range filter, which already
or-joins both namespaces, found them.

Resolve the shared bank accounts up front and pass the id set into the
existing clause rather than or-joining inside the query: an or-join turns
a selective indexed lookup into per-line work and measured 2.5-3.4x
slower on every account search, including ones that share no code. When
nothing shares the code the emitted query is unchanged, so the common
case stays at parity (0.97-1.04x, plus ~0.2ms to resolve).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 21:23:01 -07:00
19d936693a Merge pull request 'fix(reports): collapse accounts sharing a numeric code to one row' (#16) from integreat-fix-report into staging
Reviewed-on: #16
2026-08-14 16:40:45 -07:00
e9970bd41a fix(reports): collapse accounts sharing a numeric code to one row
A client's bank account and the financial account it posts to carry the
same numeric code. Reports keyed their detail rows on [code, name], so
the pair rendered as two rows — one labelled for the bank account, one
for the financial account.

The amount was duplicated too, not just the label: the row's figure is
filtered by code alone, so both rows printed the whole code's total. The
group subtotal counts the code once, so a section's visible rows stopped
footing to their own subtotal. Penelope's Coffee and Tea overshot the
2000 Accounts Payable subtotal by $251,751.80 this way.

Resolve it in two places:

- build-account-lookup now maps every account at a shared code down to
  the bank account's name, per client. Where a client has two bank
  accounts on one code, lowest :bank-account/sort-order wins, then
  lowest :db/id, so the label is stable across runs.
- used-accounts now keys rows on the code alone. Across a multi-client
  report the clients can still disagree, since only some of them have a
  bank account at the code; a bank-sourced name wins there, which the
  new :bank_account_name? flag carries through from the lookup.

Rows are code-keyed now, so detail-rows decides whether to print a
figure by asking whether the client has data at the code rather than
under the winning name — otherwise a client reaching a code under a name
another client won would blank out.

Balance sheet, profit and loss and cash flows all route through
used-accounts and are all fixed. The GraphQL and cljs balance sheets
pick up the unified name through build-account-lookup.

A sweep of all 146 clients with bank accounts finds duplicate rows on 8
of them before this change and none after, with every section total
unchanged.
2026-08-14 16:36:01 -07:00
366781e818 sort fix 2026-08-13 10:13:57 -07:00
bcb1978f44 fix(ledger): ignore external import rows with no debit or credit
A pasted row with both amount columns empty carries no accounting
information, but it still became a line item: it tripped the "Line item
amount 0.0 must be greater than 0." warning, which demoted the entire
journal entry to "ignored" -- so the good rows around it were dropped
and any existing entry with that external id was retracted.

Drop those rows in table->entries before add-errors runs, so they never
reach the balance check, :amount, or :line-items. An entry whose rows are
all blank disappears completely: not imported, not retracted, not
counted. Only genuinely empty values qualify (nil or a whitespace-only
string, since form input decodes "" to nil); an explicit 0 still warns as
before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 12:59:47 -07:00
785c6b3731 feat(ssr): add errors-only filter to the external import grids
Both external import review grids (ledger and transaction) can run to
hundreds of rows, so finding the handful that failed validation meant
scrolling the whole table. Add an "Only show errors" checkbox next to
"Show table" that hides every clean row.

Done with native Alpine: the wrapper's x-data carries errorsOnly, the
checkbox is x-model bound, and each row is rendered server-side knowing
whether it has errors -- flagged rows get no directive, clean rows get
x-show="!errorsOnly". The grid container becomes
x-show="showTable || errorsOnly" so ticking the filter reveals the table
in one click.

Submitting is unaffected: x-show only toggles display, so hidden rows
keep their inputs in the DOM and still post. Verified in the browser that
the full row set serializes while the filter is on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:30:44 -07:00
33cfbab54a fix(parse): extract Bonanza bill-to lines with mixed case and punctuation
The Bonanza Produce invoice template captured the bill-to name and street
with [A-Z\s] / [A-Z0-9\s] classes, so any store name or address containing
a lowercase letter or punctuation failed to match and came back nil. On the
McCarran invoice that meant both :customer-identifier and :account-number
were empty, leaving the import with nothing to look a client up by.

Anchor instead on the B/I/L/L letters printed down the left margin at the
start of a line (the ship-to block on the right reuses the same letters
mid-line) and take the whole column up to the next column gap, without
restricting the character set. The leading \d on the account-number capture
is what selects the street L line over the name L line.

Every value the template already extracted is unchanged; only the nils
moved. Adds regression tests for both Reno locations, covering the address
that used to drop out and the sibling one that already worked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:11:08 -07:00
e99ac6e978 fix(ssr): stop parse-sort leaking a render fn into wizard snapshots
Sorting a grid before opening a bulk wizard 500'd the submit with
"No reader function for tag object".

parse-sort returned the grid's whole :matching-header map, which carries a
:render fn. That sort rides along in :query-params, which the bulk wizards
copy verbatim into their form snapshot (bulk_code.clj:88, invoices.clj:1437).
The snapshot is serialized with pr-str into a hidden field and read back with
clojure.edn/read-string on submit; a fn pr-strs as #object[...], which edn has
no reader for, so wrap-decode-multi-form-state threw before the handler ran.

:matching-header was only ever read inside parse-sort itself - nothing
downstream consumes it, and apply-toggle-sort in this same namespace already
builds entries without it. Keep it as a local binding to derive :name and to
drop unknown columns, and leave it out of the result.

Fixes transaction bulk-code (11 production 500s over 2026-08-10/11) and the
same latent bug in invoice bulk-edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:10:26 -07:00
b43f107610 fix(ssr): make the CSV export button visible, add account number to ledger CSV
The grid export button rendered white-on-white: a-button's :secondary-light
asked for bg-white-200, which tailwind never generated (no white scale in the
config), then fell through to the generic color branch that layers text-white
on top. Give the variant a real light-blue fill and stop the fallthrough, and
label the button "CSV" so it isn't a lone download glyph.

The ledger register CSV named the account but not its number, so exports had
to be joined back by name. Add an Account Number column beside it, falling
back to the bank account's numeric code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:48:30 -07:00
379bfa78f7 feat(ssr): add clear button to typeahead
The single typeahead had no visible way to unset a selection — clearing
was only possible via the undiscoverable backspace keybinding.

Adds an x icon on the right side of the field, shown only when a value is
selected. It reuses the same `value = {value: '', label: ''}` assignment
the existing backspace handler uses, so it rides the already-wired clear
path: the hidden input's $watch dispatches `change` (driving htmx filter
refetches) and `x-modelable "value.value"` propagates the empty value to
any parent scope bound via x-model (driving dependent fetches such as the
transaction-rule Account -> Location and Client -> Bank Account chains).

The icon is a plain div with tabindex="-1" and aria-hidden, so it stays
out of the tab order; @click.prevent.stop keeps the click from bubbling
to the wrapping anchor and popping the dropdown. Matches the clear
affordance multi-typeahead already had.

Rebuilds output.css for the new dark:hover:text-gray-200 utility.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:32:25 -07:00
0575c88c4d cleans up ux. 2026-08-04 23:33:50 -07:00
bc2abf4cb1 fixes 2026-08-04 23:00:29 -07:00
acc5a7aa1b Merge branch 'staging' of gitea.story-basking.ts.net:notid/integreat into staging 2026-08-04 22:59:46 -07:00
46fdc29712 Merge pull request 'integreat-send-email' (#15) from integreat-send-email into staging
Reviewed-on: #15
2026-08-04 22:59:33 -07:00
0df9a29022 feat(ssr): add report email hand-off to ledger export modal
The SSR ledger reports could generate and download a PDF but had lost the
SPA's ability to hand the report off to the client's email contacts. This
restores it for Profit and Loss, Balance Sheet and Cash Flows.

The server still sends nothing: the modal offers a mailto: link, pre-filled
with the client's email contacts, subject and body, that opens in the user's
own mail client so they can review before sending.

Extracts the modal the three reports duplicated into a shared
auto-ap.ssr.ledger.export-modal namespace, and tightens the SPA's rules
along the way:

- admin-only, uniformly (the SPA's Cash Flows page skipped this check)
- single client only, uniformly (the SPA's Balance Sheet page did not check,
  so a multi-client report could be mailed to one client's contacts)
- recipients joined with "," per RFC 6068 rather than Outlook's ";"
- subject percent-encoded, like the body already was
- body links built from :base-url and bidi routes, fixing the dead
  /reports/ link (the page now lives at /company/reports) and the
  hardcoded prod domain in the requires-feedback link

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:55:53 -07:00
a17e16e31b feat(ledger): remove a whole ledger entry from the external import grid
The external ledger import review grid is one row per line item, so a
journal entry spans two or more rows. When one entry fails validation the
whole paste is rejected, and the only way forward was to re-paste without
it.

Each row now carries its entry id (client-source-externalId, the same key
table->entries groups on) and the last column gets a trash button that
drops every row sharing that id. The rows are the form inputs, so removing
them from the DOM removes them from the next import post -- which also
works for rows that fail schema validation. Index gaps left behind are
compacted by coerce-vector on the way back in.

Also drops two leftover pprint calls that dumped form-errors to stdout on
every render.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:44:52 -07:00
e6507d5387 merged 2026-08-04 22:15:59 -07:00
5126f3799d data(sysco): apply client category verification sheet to line-item mapping
Recategorizes 131 Sysco line-item descriptions per the client-reviewed
"Product Category Verification with codes" sheet: 96 existing rows re-coded
and 35 new rows appended (ids 1796-1830).

Root cause this addresses: get-line-account matches on exact description
string and silently defaults anything unmapped to 50000 Food Costs. Only 147
of the sheet's 321 reviewed rows coded the way the client expected. Note the
sheet's "change" column understates the work -- 20 of its 53 change rows are
no-ops (Paper -> Paper, confirming the gloves/liners/hairnets) and 3 were
already fixed in 38575aa5 / 7a0e256f, while 214,398 lines of movement come
from rows the client ticked as correct against a suggestion that already
differed from production.

Moves, replayed over all 1,022,732 DET lines in sysco-poller:

  50000 -> 51500 Dry Goods            90,481 ln   $5,215,011.82  105 clients
  50000 -> 51450 Dressing & Sauce     58,191 ln   $4,445,726.56   98
  50000 -> 51400 Bread and Bun        37,705 ln   $3,964,462.24   96
  50000 -> 52000 Soft Beverage        36,244 ln   $1,033,497.52   94
  50000 -> 51200 Produce               6,519 ln     $460,629.32   98
  55000 -> 51500 Dry Goods             6,516 ln     $259,750.60   97
  50000 -> 74100 Cleaning Supplies     5,630 ln     $205,233.19   98
  55000 -> 74100 Cleaning Supplies     5,222 ln     $125,946.32   99
  50000 -> 51120 Chicken/Poultry         265 ln      $42,482.50    8
  50000 -> 51300 Dairy                    36 ln       $5,941.69    6
  50000 -> 55000 Paperware                54 ln       $1,751.22   18
  54400 -> 51450 Dressing & Sauce         24 ln       $1,413.26    1
  total                               246,887 ln  $15,761,846.24

Only three source accounts are touched: 50000 and 55000 (the two silent
defaults) plus the single intended 54400 -> 51450 vinaigrette row. Nothing
else leaves a deliberately assigned account.

The 7 Misc Charges descriptions are deliberately left alone per Bryce,
including PICKLE CHIP KOSH 1/4 KK, which therefore stays at the 50000
default rather than moving to Produce as the sheet originally suggested.

Also corrects the PAPER & DISP fallback comment in sysco.clj: 440 of its 455
mapped descriptions point at 55000, not all 454. The 15 exceptions (foil
pans -> 51500, scour pads -> 74100) are mapped explicitly, so the
description map still wins ahead of the fallback. No logic changed.

Affects only clients with the code-sysco-items feature flag, and only at
import time -- already-imported invoices keep their existing splits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:00:57 -07:00
7a0e256f07 fix(sysco): fall back to PAPER & DISP category when description is unmapped
The Sysco importer codes each line item by exact description match against
resources/sysco_line_item_mapping.csv, silently defaulting to GL 50000 (Food
Costs) when the description is absent. Every new or renamed Sysco SKU
therefore leaks into Food Costs until someone hand-patches the CSV, which is
what 38575aa5 did for 34 descriptions.

Add a category-level fallback consulted after the description map and before
the 50000 default, enabled for PAPER & DISP only. The description mapping
still wins wherever it exists, so nothing already mapped changes.

PAPER & DISP is safe to generalize: all 454 mapped PAPER & DISP rows point at
55000, with no exceptions. Of the 852 distinct descriptions ever invoiced
under that category, only 3 resolved elsewhere, each because a row with a
different category shared the description and won the later-wins (into {}).
One of those, DESSERT CUP, was simply mis-categorized -- it is paper, and its
own lid (id 1782 LID DOME DESSERT CUP) was already 55000 -- so correct id 1772
to PAPER & DISP / 55000. The remaining two stay at 50000 on purpose, since
they are not paper: PAD SCRUB S-S 35 GRAM 1.25 OZ (SUPP & EQUIP) and TEST
STRIP SANITIZER QUAT (CHEMICAL/JANTRL).

Verified by replaying both changes over all 1,022,732 DET lines in the 56,010
CSVs under sysco-poller/: every resulting transition is 50000 -> 55000 (10,994
lines, $728,106.62). No line that already resolved to a non-default account
moved.

Note this only affects clients carrying the code-sysco-items feature flag, and
only on import -- already-imported invoices need a separate recode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 07:43:58 -07:00
7155e199e8 Merge branch 'master' into staging 2026-07-29 10:25:04 -07:00
2b5fbaca00 Add template for new Reel Produce statement layout
The statement no longer prints "Reel Produce" as text (only
orders@reelproduce.com), switched to MM/DD/YYYY dates, and moved the
invoice number into an "INV #..." transaction description, so no
template matched and the file fell through to the glimpse2 fallback.

Adds a QuickBooks-statement-style template (same shape as Suncrest /
Ocean Queen) keyed on reelproduce.com + Statement, placed after the
existing Reel Produce statement template so the old layout still wins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 10:24:31 -07:00
473556a45d Backfill script for olo 2026-07-29 09:59:02 -07:00
604d1ee1cf changes 2026-07-25 21:13:44 -07:00
e095cb94e4 fix(config): propagate rotated Plaid secret to worker configs
The rotation in 111eca41 updated the Plaid secret-key only in prod.edn,
leaving prod-background-worker.edn, prod-cloud-background-worker.edn, and
prod-cloud.edn on the old (now-invalidated) secret. Background worker jobs
loading those files failed with INVALID_API_KEYS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 20:29:15 -07:00
9d007ee8e2 Merge branch 'master' into staging 2026-07-22 21:50:12 -07:00
fa25620b7a olo fixes 2026-07-22 21:49:24 -07:00
d012283362 olo fixes 2026-07-22 21:43:31 -07:00
14726d0208 re-synced secrets. 2026-07-22 21:17:31 -07:00
6429cf823d fixes 2026-07-12 20:45:36 -07:00
111eca413e rotates passwords 2026-07-12 20:44:05 -07:00
80e45c026e feat(ssr): add sales summary csv download
Exports one row per sales summary item so each category is individually
auditable, rather than one row per daily summary. Uses the grid helper's
page->csv-entities hook, the same seam the ledger export uses to fan a
journal entry out into its line items.

Includes the GL account code alongside the name so rows tie back to the
chart of accounts, and formats amounts to cents to keep float noise
(36.900000000000006) out of the export. Accounts are resolved in a single
batched pull and clientized per summary so name overrides are respected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:51:46 -07:00
9cd3d9c962 fixed. 2026-07-08 23:33:37 -07:00
5f5686c39c fix(ssr): link to the real transaction route from paperclip menus
::transaction-routes/all-page does not exist -- the transactions index
is registered as ::transaction-routes/page ("/transaction2"). bidi's
path-for returns nil for an unknown handler, so (hu/url nil {...})
produced the relative "?exact-match-id=123". Clicking a Transaction
link in the ledger paperclip tooltip therefore stayed on the ledger and
re-filtered it by a transaction entity id, matching nothing.

Repoint all five call sites (ledger, payments and invoice paperclip
menus, the insights breadcrumb, and the expected-deposit row button) at
::transaction-routes/page.

Also add the missing conj in the ledger :invoice/source-url cond->
branch. The bare map made cond-> invoke it as a function against the
accumulated vector, yielding nil, so any journal entry whose original
entity was an invoice with a file rendered no paperclip at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:15:17 -07:00
d0846f91aa fix(ssr): use form-validation-error for transaction edit validations
Untyped (ex-info ... {:validation-error ...}) throws escaped the
wrap-form-4xx-2 middleware, surfacing as a 500 / unexpectedError box
instead of an inline form error. Route them through
utils/form-validation-error, which throws with :type :form-validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:12:00 -07:00
56addf8c70 fix(ssr): order coerced vector form params by numeric index
Indexed form fields like periods[0]..periods[13] parse into a
string-keyed map; coerce-vector rebuilt the vector with a plain
lexicographic sort, so "10".."13" sorted before "2". Reports with
10+ periods (e.g. the 13/14-period option) rendered columns in the
order 0,1,10,11,12,13,2..9. Sort index keys numerically instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 05:22:52 -07:00
ebb48a0de4 feat(ssr): previous calendar year period + external register filters
- P&L period dropdown: replace "Calendar year (YYYY)" with
  "Previous Calendar Year (YYYY-1)" pulling the full prior year
- External register: title now reads "External Register"; add
  Source, External Id, and Location filters

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:22:31 -07:00
fc54b92ddb fixes 2026-06-04 22:59:51 -07:00
019a1b4cd8 fixes 2026-06-04 22:56:01 -07:00
38575aa5bd data(sysco): add missing line-item GL mappings for paper & other items
The Sysco importer codes line items by exact-matching the item description
against resources/sysco_line_item_mapping.csv, falling back to GL 50000
(Food Costs) when no entry exists. On master, 8 of the paper-product
descriptions on recent invoices (e.g. BAG PAPER 250 CT, NAPKIN 2PLY INTR
FOLD 6.3X8.26, CONTAINER PAPER 4/110OZ NTG) were missing, so they
defaulted to 50000 instead of 55000 (Paper Costs).

Append the 34 curated mappings (Ids 1762-1795) covering these paper items
(-> 55000) plus the other new items from the same invoices, so they code
correctly on re-import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:28:23 -07:00
51 changed files with 2295 additions and 368 deletions

View File

@@ -173,4 +173,4 @@
:token "EAAAEO2xSqesDutZz71hz3eulKmrlKTiEqG3uZ4j25x5GYlOluQ2cj2JxNUXqXD7"}} :token "EAAAEO2xSqesDutZz71hz3eulKmrlKTiEqG3uZ4j25x5GYlOluQ2cj2JxNUXqXD7"}}
:plaid {:base-url "https://production.plaid.com" :plaid {:base-url "https://production.plaid.com"
:client-id "61bfab05f7e762001b323f79" :client-id "61bfab05f7e762001b323f79"
:secret-key "2be026ca5e7f7e9f23f2fb4d7c914d"}} :secret-key "44a05fbe9f33a2975b3b3ac06b0b62"}}

View File

@@ -31,5 +31,5 @@
:yodlee2-proxy-port 8888 :yodlee2-proxy-port 8888
:plaid {:base-url "https://production.plaid.com" :plaid {:base-url "https://production.plaid.com"
:client-id "61bfab05f7e762001b323f79" :client-id "61bfab05f7e762001b323f79"
:secret-key "2be026ca5e7f7e9f23f2fb4d7c914d"} :secret-key "44a05fbe9f33a2975b3b3ac06b0b62"}
} }

View File

@@ -34,5 +34,5 @@
:yodlee2-proxy-port 8888 :yodlee2-proxy-port 8888
:plaid {:base-url "https://production.plaid.com" :plaid {:base-url "https://production.plaid.com"
:client-id "61bfab05f7e762001b323f79" :client-id "61bfab05f7e762001b323f79"
:secret-key "2be026ca5e7f7e9f23f2fb4d7c914d"} :secret-key "44a05fbe9f33a2975b3b3ac06b0b62"}
} }

View File

@@ -6,7 +6,7 @@
:scheme "https" :scheme "https"
:dd-env "prod" :dd-env "prod"
:dd-service "integreat-app" :dd-service "integreat-app"
:jwt-secret "auto ap invoices are awesome" :jwt-secret "rotated secrets are the best"
:invoice-import-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-mail-prod" :invoice-import-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-mail-prod"
:requests-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-background-request-prod" :requests-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-background-request-prod"
:invoice-email "invoices@mail.app.integreatconsult.com" :invoice-email "invoices@mail.app.integreatconsult.com"
@@ -25,12 +25,12 @@
:run-background? false :run-background? false
:run-web? true :run-web? true
:yodlee2-integreat-user "integreat-main" :yodlee2-integreat-user "integreat-main"
:yodlee2-client-id "3AATcwfPsWP1rP9oDoo4HvZhtaroGVcA" :yodlee2-client-id "lNgzSdjkVXSkXyOBlResXUsCpCpBBDlG"
:yodlee2-client-secret "cXTBmKbGfkaBFIpM" :yodlee2-client-secret "U3qjZP2gErZfbuTPud+LNJF9jHbNRzCWCZbEi6dDiHsziCNwI5yNNNrBAUsnjcu7VFsUVNmkxNKSW85Qf1YMOITC7q0kdv7MGv/ZqRLfQV5odkiPbLHgOrE7UE6//MtjU0jTznGA70WTPS+wwmugg8ArNnx+4QCHrrBrkRfFVOE="
:yodlee2-base-url "https://production.api.yodlee.com/ysl" :yodlee2-base-url "https://production.api.yodlee.com/ysl"
:yodlee2-fastlink "https://fl4.prod.yodlee.com/authenticate/USDevexProd2-319/fastlink/?channelAppName=usdevexprod2" :yodlee2-fastlink "https://fl4.prod.yodlee.com/authenticate/USDevexProd2-319/fastlink/?channelAppName=usdevexprod2"
:yodlee2-proxy-host "172.31.10.83" :yodlee2-proxy-host "172.31.10.83"
:yodlee2-proxy-port 8888 :yodlee2-proxy-port 8888
:plaid {:base-url "https://production.plaid.com" :plaid {:base-url "https://production.plaid.com"
:client-id "61bfab05f7e762001b323f79" :client-id "61bfab05f7e762001b323f79"
:secret-key "2be026ca5e7f7e9f23f2fb4d7c914d"}} :secret-key "44a05fbe9f33a2975b3b3ac06b0b62"}}

View File

@@ -6,7 +6,7 @@
:scheme "https" :scheme "https"
:dd-env "prod" :dd-env "prod"
:dd-service "integreat-app" :dd-service "integreat-app"
:jwt-secret "auto ap invoices are awesome" :jwt-secret "rotated secrets are the best"
:invoice-import-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-mail-prod" :invoice-import-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-mail-prod"
:requests-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-background-request-prod" :requests-queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-background-request-prod"
:invoice-email "invoices@mail.app.integreatconsult.com" :invoice-email "invoices@mail.app.integreatconsult.com"
@@ -25,13 +25,13 @@
:run-background? false :run-background? false
:run-web? true :run-web? true
:yodlee2-integreat-user "integreat-main" :yodlee2-integreat-user "integreat-main"
:yodlee2-client-id "3AATcwfPsWP1rP9oDoo4HvZhtaroGVcA" :yodlee2-client-id "lNgzSdjkVXSkXyOBlResXUsCpCpBBDlG"
:yodlee2-client-secret "cXTBmKbGfkaBFIpM" :yodlee2-client-secret "U3qjZP2gErZfbuTPud+LNJF9jHbNRzCWCZbEi6dDiHsziCNwI5yNNNrBAUsnjcu7VFsUVNmkxNKSW85Qf1YMOITC7q0kdv7MGv/ZqRLfQV5odkiPbLHgOrE7UE6//MtjU0jTznGA70WTPS+wwmugg8ArNnx+4QCHrrBrkRfFVOE="
:yodlee2-base-url "https://production.api.yodlee.com/ysl" :yodlee2-base-url "https://production.api.yodlee.com/ysl"
:yodlee2-fastlink "https://fl4.prod.yodlee.com/authenticate/USDevexProd2-319/fastlink/?channelAppName=usdevexprod2" :yodlee2-fastlink "https://fl4.prod.yodlee.com/authenticate/USDevexProd2-319/fastlink/?channelAppName=usdevexprod2"
:yodlee2-proxy-host "172.31.10.83" :yodlee2-proxy-host "172.31.10.83"
:yodlee2-proxy-port 8888 :yodlee2-proxy-port 8888
:plaid {:base-url "https://production.plaid.com" :plaid {:base-url "https://production.plaid.com"
:client-id "61bfab05f7e762001b323f79" :client-id "61bfab05f7e762001b323f79"
:secret-key "2be026ca5e7f7e9f23f2fb4d7c914d"} :secret-key "44a05fbe9f33a2975b3b3ac06b0b62"}
} }

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
1`

View File

@@ -0,0 +1 @@
,noti,pop-os,01.06.2026 21:02,file:///home/noti/.config/libreoffice/4;

View File

@@ -268,6 +268,21 @@ const calendarYearPeriod = (date) => {
return {end: formatDateMMDDYYYY(end), start: formatDateMMDDYYYY(start)}; return {end: formatDateMMDDYYYY(end), start: formatDateMMDDYYYY(start)};
} }
const previousCalendarYearPeriod = (date) => {
if (!date) {
date= new Date()
} else {
date = parseMMDDYYYY(date)
}
const priorYear = date.getFullYear() - 1;
// Jan 1 - Dec 31 of the previous calendar year
const start = new Date(priorYear, 0, 1);
const end = new Date(priorYear, 11, 31);
return {end: formatDateMMDDYYYY(end), start: formatDateMMDDYYYY(start)};
}
const getLastMonthPeriods = (date) => { const getLastMonthPeriods = (date) => {
if (!date) { if (!date) {
date = new Date(); date = new Date();

File diff suppressed because one or more lines are too long

View File

@@ -6,10 +6,10 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
4,PAPER & DISP,STRAW PLAS TRANS JMB WRPD 7.75,Paper Costs,55000, 4,PAPER & DISP,STRAW PLAS TRANS JMB WRPD 7.75,Paper Costs,55000,
5,PAPER & DISP,FORK PLAS BLK PLA COMPOSTABLE,Paper Costs,55000, 5,PAPER & DISP,FORK PLAS BLK PLA COMPOSTABLE,Paper Costs,55000,
6,PAPER & DISP,BAG PLAS LOGO 3 CLR,Paper Costs,55000, 6,PAPER & DISP,BAG PLAS LOGO 3 CLR,Paper Costs,55000,
7,FROZEN,BREAD PITA GYRO PRE-OILED 7,Food Costs,50000, 7,FROZEN,BREAD PITA GYRO PRE-OILED 7,Bread and Bun Costs,51400,
8,DAIRY PRODUCTS,YOGURT FRZN TART,Dairy Costs,51300, 8,DAIRY PRODUCTS,YOGURT FRZN TART,Dairy Costs,51300,
9,POULTRY,GYRO CHICKEN SHAWARMA CONE,Chicken/ Poultry Costs,51120, 9,POULTRY,GYRO CHICKEN SHAWARMA CONE,Chicken/ Poultry Costs,51120,
10,FROZEN,BAKLAVA CLASSIC 2X24,Food Costs,50000, 10,FROZEN,BAKLAVA CLASSIC 2X24,Dry Goods Costs,51500,
11,MEATS,PORK SLI GYRO CONE,Beef/Pork Costs,51110, 11,MEATS,PORK SLI GYRO CONE,Beef/Pork Costs,51110,
12,DAIRY PRODUCTS,SAUCE TZATZIKI,Dairy Costs,51300, 12,DAIRY PRODUCTS,SAUCE TZATZIKI,Dairy Costs,51300,
13,POULTRY,CHICKEN CVP THIGH BNLS SKLS,Chicken/ Poultry Costs,51120, 13,POULTRY,CHICKEN CVP THIGH BNLS SKLS,Chicken/ Poultry Costs,51120,
@@ -19,7 +19,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
17,CANNED AND DRY,RICE BASMATI STEAMED XTRA LNG,Food Costs,50000, 17,CANNED AND DRY,RICE BASMATI STEAMED XTRA LNG,Food Costs,50000,
18,CANNED AND DRY,WATER SPARKLING GREEK,Beverages Costs,52000, 18,CANNED AND DRY,WATER SPARKLING GREEK,Beverages Costs,52000,
19,DAIRY PRODUCTS,SAUCE SPICY YOGURT LOGO,Dairy Costs,51300, 19,DAIRY PRODUCTS,SAUCE SPICY YOGURT LOGO,Dairy Costs,51300,
20,CANNED AND DRY,WATER PURIFIED .5,Food Costs,50000, 20,CANNED AND DRY,WATER PURIFIED .5,Soft Beverage Cost,52000,
21,FROZEN,DOUGH PASTRY HNY PUFF,Food Costs,50000, 21,FROZEN,DOUGH PASTRY HNY PUFF,Food Costs,50000,
22,DAIRY PRODUCTS,YOGURT PLAIN GREEK NON-FAT,Dairy Costs,51300, 22,DAIRY PRODUCTS,YOGURT PLAIN GREEK NON-FAT,Dairy Costs,51300,
23,PAPER & DISP,GLOVE NITRILE LARGE,Paper Costs,55000, 23,PAPER & DISP,GLOVE NITRILE LARGE,Paper Costs,55000,
@@ -43,39 +43,39 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
41,CANNED AND DRY,WATER BOTTLED DRINKING,Food Costs,50000, 41,CANNED AND DRY,WATER BOTTLED DRINKING,Food Costs,50000,
42,CANNED AND DRY,SODA CHERRY VISSINADA GRK PLAS,Food Costs,50000, 42,CANNED AND DRY,SODA CHERRY VISSINADA GRK PLAS,Food Costs,50000,
43,CANNED AND DRY,SODA LEMON LEMONADA GREEK,Soft Beverage Costs,52000, 43,CANNED AND DRY,SODA LEMON LEMONADA GREEK,Soft Beverage Costs,52000,
44,CANNED AND DRY,WATER MINERAL CARNONATED GREEK,Food Costs,50000, 44,CANNED AND DRY,WATER MINERAL CARNONATED GREEK,Soft Beverage Cost,52000,
45,DISPENSER BEVRG,SYRUP COLA PEPSI BIB,Soft Beverage Costs,52000, 45,DISPENSER BEVRG,SYRUP COLA PEPSI BIB,Soft Beverage Costs,52000,
46,DISPENSER BEVRG,SYRUP LEMONADE PNK BIB,Soft Beverage Costs,52000, 46,DISPENSER BEVRG,SYRUP LEMONADE PNK BIB,Soft Beverage Costs,52000,
47,CANNED AND DRY,RICE BASMATI PABROIL SELA CS,Food Costs,50000, 47,CANNED AND DRY,RICE BASMATI PABROIL SELA CS,Dry Goods Costs,51500,
48,CANNED AND DRY,KETCHUP FANCY,Food Costs,50000, 48,CANNED AND DRY,KETCHUP FANCY,Dry Goods Costs,51500,
49,CANNED AND DRY,TUB & HUMMUS,Food Costs,50000, 49,CANNED AND DRY,TUB & HUMMUS,Food Costs,50000,
50,CHEMICAL/JANTRL,SANITIZER MULTI QUAT LIQ,Food Costs,50000, 50,CHEMICAL/JANTRL,SANITIZER MULTI QUAT LIQ,Cleaning Supplies,74100,
51,DAIRY PRODUCTS,YOGURT PLAIN GRK 5%,Dairy Costs,51300, 51,DAIRY PRODUCTS,YOGURT PLAIN GRK 5%,Dairy Costs,51300,
52,FROZEN,APTZR VEG FALAFEL BALL,Food Costs,50000, 52,FROZEN,APTZR VEG FALAFEL BALL,Dry Goods Costs,51500,
53,PAPER & DISP,BOWL PAPER FIBER RND 32OZ 8IN,Paper Costs,55000, 53,PAPER & DISP,BOWL PAPER FIBER RND 32OZ 8IN,Paper Costs,55000,
54,PAPER & DISP,LID PLAS F/BOWL RND 8,Paper Costs,55000, 54,PAPER & DISP,LID PLAS F/BOWL RND 8,Paper Costs,55000,
55,MEATS,BEEF GRND CHUCK FINE 80/20FRSH,Beef/Pork Costs,51110, 55,MEATS,BEEF GRND CHUCK FINE 80/20FRSH,Beef/Pork Costs,51110,
56,CANNED AND DRY,SODA ORANGE CRSH,Food Costs,50000, 56,CANNED AND DRY,SODA ORANGE CRSH,Soft Beverage Cost,52000,
57,PAPER & DISP,CONTAINER PLAS CLR BAR LK 5 IN,Paper Costs,55000, 57,PAPER & DISP,CONTAINER PLAS CLR BAR LK 5 IN,Paper Costs,55000,
58,CANNED AND DRY,KETCHUP PACKET FCY,Food Costs,50000, 58,CANNED AND DRY,KETCHUP PACKET FCY,Dry Goods Costs,51500,
59,PAPER & DISP,BAG PLAS WAVE TOP LOGO 18X16,Paper Costs,55000, 59,PAPER & DISP,BAG PLAS WAVE TOP LOGO 18X16,Paper Costs,55000,
60,CANNED AND DRY,DRESSING VINAIGRETTE LOGO,Food Costs,50000, 60,CANNED AND DRY,DRESSING VINAIGRETTE LOGO,Dressing & Sauce Cost,51450,
61,PAPER & DISP,CONTAINER PAPER MLD FBR 9X6,Paper Costs,55000, 61,PAPER & DISP,CONTAINER PAPER MLD FBR 9X6,Paper Costs,55000,
62,PAPER & DISP,BOWL PAPER MLD FBR 32OZ NFA,Paper Costs,55000, 62,PAPER & DISP,BOWL PAPER MLD FBR 32OZ NFA,Paper Costs,55000,
63,PAPER & DISP,CONTAINER PLAS 120Z SUNDAE,Paper Costs,55000, 63,PAPER & DISP,CONTAINER PLAS 120Z SUNDAE,Paper Costs,55000,
64,CANNED AND DRY,DRESSING VINAIGRETTE GYRO,Food Costs,50000, 64,CANNED AND DRY,DRESSING VINAIGRETTE GYRO,Food Costs,50000,
65,CANNED AND DRY,VINEGAR WINE RED 5% 50 GRN,Alcohol Costs,54000, 65,CANNED AND DRY,VINEGAR WINE RED 5% 50 GRN,Alcohol Costs,54000,
66,DAIRY PRODUCTS,EGG SHELL LG WHT AA CA CGFREE,Dairy Costs,51300, 66,DAIRY PRODUCTS,EGG SHELL LG WHT AA CA CGFREE,Dairy Costs,51300,
67,CANNED AND DRY,DRESSING MARINADE SOUVLAKI,Food Costs,50000, 67,CANNED AND DRY,DRESSING MARINADE SOUVLAKI,Dressing & Sauce Cost,51450,
68,CANNED AND DRY,SAUCE MUSTARD,Food Costs,50000, 68,CANNED AND DRY,SAUCE MUSTARD,Dressing & Sauce Cost,51450,
69,CANNED AND DRY,TEA ICED SWEET PURELEAF,Beverages Costs,52000, 69,CANNED AND DRY,TEA ICED SWEET PURELEAF,Beverages Costs,52000,
70,PAPER & DISP,LINER TRASH 40X46 1.1 ML GRY,Paper Costs,55000, 70,PAPER & DISP,LINER TRASH 40X46 1.1 ML GRY,Paper Costs,55000,
71,CANNED AND DRY,SODA COLA,Soft Beverage Costs,52000, 71,CANNED AND DRY,SODA COLA,Soft Beverage Costs,52000,
72,CANNED AND DRY,HONEY PURE CLOVER GR A TSC JUG,Food Costs,50000, 72,CANNED AND DRY,HONEY PURE CLOVER GR A TSC JUG,Dry Goods Costs,51500,
73,DAIRY PRODUCTS,CHEESE FETA RW,Dairy Costs,51300, 73,DAIRY PRODUCTS,CHEESE FETA RW,Dairy Costs,51300,
74,CANNED AND DRY,WATER PURIFIED BTL PET LSE DW,Food Costs,50000, 74,CANNED AND DRY,WATER PURIFIED BTL PET LSE DW,Soft Beverage Cost,52000,
75,PRODUCE,JUICE LEMON FRESH PSTRZD,Produce Costs,51200, 75,PRODUCE,JUICE LEMON FRESH PSTRZD,Produce Costs,51200,
76,CANNED AND DRY,SPREAD HUMMUS TRADITIONAL,Food Costs,50000, 76,CANNED AND DRY,SPREAD HUMMUS TRADITIONAL,Dressing & Sauce Cost,51450,
77,PRODUCE,LETTUCE ROMAINE OF HEART FRSH,Produce Costs,51200, 77,PRODUCE,LETTUCE ROMAINE OF HEART FRSH,Produce Costs,51200,
78,PAPER & DISP,CONTAINER PAPER #1/30OZ NTG,Paper Costs,55000, 78,PAPER & DISP,CONTAINER PAPER #1/30OZ NTG,Paper Costs,55000,
79,CANNED AND DRY,OIL SALAD CANOLA ZTF,Food Costs,50000, 79,CANNED AND DRY,OIL SALAD CANOLA ZTF,Food Costs,50000,
@@ -84,7 +84,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
82,POULTRY,CHICKEN CVP WHL WOG NAE 3.5-4#,Chicken/ Poultry Costs,51120, 82,POULTRY,CHICKEN CVP WHL WOG NAE 3.5-4#,Chicken/ Poultry Costs,51120,
83,PAPER & DISP,GLOVE NITRILE FDSRV PF BLU LRG,Paper Costs,55000, 83,PAPER & DISP,GLOVE NITRILE FDSRV PF BLU LRG,Paper Costs,55000,
84,CANNED AND DRY,RICE BASMATI CHEF SECRT LG GRN,Food Costs,50000, 84,CANNED AND DRY,RICE BASMATI CHEF SECRT LG GRN,Food Costs,50000,
85,FROZEN,APTZR VEG FALAFEL PUCK HALAL,Food Costs,50000, 85,FROZEN,APTZR VEG FALAFEL PUCK HALAL,Dry Goods Costs,51500,
86,PAPER & DISP,LID PLAS PET FOR 32OZ BOWL,Paper Costs,55000, 86,PAPER & DISP,LID PLAS PET FOR 32OZ BOWL,Paper Costs,55000,
87,PAPER & DISP,FORK PLAS PP X-HVY BLK,Paper Costs,55000, 87,PAPER & DISP,FORK PLAS PP X-HVY BLK,Paper Costs,55000,
88,PRODUCE,TOMATO ROMA JUMBO FRESH,Produce Costs,51200, 88,PRODUCE,TOMATO ROMA JUMBO FRESH,Produce Costs,51200,
@@ -95,7 +95,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
93,PRODUCE,SPINACH BABY FRSH,Produce Costs,51200, 93,PRODUCE,SPINACH BABY FRSH,Produce Costs,51200,
94,PRODUCE,DILL BABY FRESH HERB,Produce Costs,51200, 94,PRODUCE,DILL BABY FRESH HERB,Produce Costs,51200,
95,PRODUCE,CUCUMBER ENGLISH MED SEEDLESS,Produce Costs,51200, 95,PRODUCE,CUCUMBER ENGLISH MED SEEDLESS,Produce Costs,51200,
96,CANNED AND DRY,SODA ORANGE PORTOKALADA GREEK,Food Costs,50000, 96,CANNED AND DRY,SODA ORANGE PORTOKALADA GREEK,Soft Beverage Cost,52000,
97,DAIRY PRODUCTS,BUTTER SOLID USDA AA UNSLTD,Dairy Costs,51300, 97,DAIRY PRODUCTS,BUTTER SOLID USDA AA UNSLTD,Dairy Costs,51300,
98,DAIRY PRODUCTS,CHEESE MONT JACK SLI INT .75OZ,Dairy Costs,51300, 98,DAIRY PRODUCTS,CHEESE MONT JACK SLI INT .75OZ,Dairy Costs,51300,
99,DAIRY PRODUCTS,CREAMER HALF & HALF SHF STBL,Dairy Costs,51300, 99,DAIRY PRODUCTS,CREAMER HALF & HALF SHF STBL,Dairy Costs,51300,
@@ -115,18 +115,18 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
113,CANNED AND DRY,FLOUR ALL PURP H&R BL EN MT,Food Costs,50000, 113,CANNED AND DRY,FLOUR ALL PURP H&R BL EN MT,Food Costs,50000,
114,CANNED AND DRY,JAM STRAWBERRY CUP,Food Costs,50000, 114,CANNED AND DRY,JAM STRAWBERRY CUP,Food Costs,50000,
115,CANNED AND DRY,MARMALADE ORANGE CUP,Food Costs,50000, 115,CANNED AND DRY,MARMALADE ORANGE CUP,Food Costs,50000,
116,CANNED AND DRY,SALT GRANULATED PLAIN,Food Costs,50000, 116,CANNED AND DRY,SALT GRANULATED PLAIN,Dry Goods Costs,51500,
117,CANNED AND DRY,SAUCE HOT PEPPER CALIFN STYLE,Food Costs,50000, 117,CANNED AND DRY,SAUCE HOT PEPPER CALIFN STYLE,Food Costs,50000,
118,CANNED AND DRY,SAUCE STEAK GLASS,Food Costs,50000, 118,CANNED AND DRY,SAUCE STEAK GLASS,Food Costs,50000,
119,CANNED AND DRY,SHORTENING PAN & GRILL,Food Costs,50000, 119,CANNED AND DRY,SHORTENING PAN & GRILL,Food Costs,50000,
120,CANNED AND DRY,SUGAR GRANULATED XFINE CANE,Food Costs,50000, 120,CANNED AND DRY,SUGAR GRANULATED XFINE CANE,Dry Goods Costs,51500,
121,CANNED AND DRY,SYRUP BREAKFAST CUP,Food Costs,50000, 121,CANNED AND DRY,SYRUP BREAKFAST CUP,Food Costs,50000,
122,CANNED AND DRY,SYRUP PANCAKE & WAFFLE,Food Costs,50000, 122,CANNED AND DRY,SYRUP PANCAKE & WAFFLE,Food Costs,50000,
123,PAPER & DISP,BAG PLAS TSHRT 11.5X6.5X21 TKU,Paper Costs,55000, 123,PAPER & DISP,BAG PLAS TSHRT 11.5X6.5X21 TKU,Paper Costs,55000,
124,PAPER & DISP,CONTAINER PLAS DELI TRANS W/LD,Paper Costs,55000, 124,PAPER & DISP,CONTAINER PLAS DELI TRANS W/LD,Paper Costs,55000,
125,PAPER & DISP,CONTAINER PLAS HNG WHT 8.5 1C,Paper Costs,55000, 125,PAPER & DISP,CONTAINER PLAS HNG WHT 8.5 1C,Paper Costs,55000,
126,PAPER & DISP,CUP PLAS PRTN TRANS 2OZ,Paper Costs,55000, 126,PAPER & DISP,CUP PLAS PRTN TRANS 2OZ,Paper Costs,55000,
127,PAPER & DISP,FOIL ALMN ROLL STD WGT 500FT,Paper Costs,55000, 127,PAPER & DISP,FOIL ALMN ROLL STD WGT 500FT,Dry Goods Costs,51500,
128,PAPER & DISP,LINER TRASH 40X46 1.6 ML BLK,Paper Costs,55000, 128,PAPER & DISP,LINER TRASH 40X46 1.6 ML BLK,Paper Costs,55000,
129,PAPER & DISP,TOWEL MULTI 9.5X9.12 EARTH+,Paper Costs,55000, 129,PAPER & DISP,TOWEL MULTI 9.5X9.12 EARTH+,Paper Costs,55000,
130,PRODUCE,ASPARAGUS FRESH LARGE FX,Produce Costs,51200, 130,PRODUCE,ASPARAGUS FRESH LARGE FX,Produce Costs,51200,
@@ -155,7 +155,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
153,DAIRY PRODUCTS,MILK WHL CORRUGATE,Dairy Costs,51300, 153,DAIRY PRODUCTS,MILK WHL CORRUGATE,Dairy Costs,51300,
154,DAIRY PRODUCTS,BUTTERMILK 1% HG,Dairy Costs,51300, 154,DAIRY PRODUCTS,BUTTERMILK 1% HG,Dairy Costs,51300,
155,DAIRY PRODUCTS,CHEESE CHDR MLD SLI INT .75 YL,Dairy Costs,51300, 155,DAIRY PRODUCTS,CHEESE CHDR MLD SLI INT .75 YL,Dairy Costs,51300,
156,CHEMICAL/JANTRL,BLEACH LIQ GRMCDL ULTRA 6%,Food Costs,50000, 156,CHEMICAL/JANTRL,BLEACH LIQ GRMCDL ULTRA 6%,Cleaning Supplies,74100,
157,POULTRY,TURKEY BRST NAT BRN PAN SKON,Poultry Costs,51120, 157,POULTRY,TURKEY BRST NAT BRN PAN SKON,Poultry Costs,51120,
158,DAIRY PRODUCTS,CREAM HEAVY 40% FRESH HG,Dairy Costs,51300, 158,DAIRY PRODUCTS,CREAM HEAVY 40% FRESH HG,Dairy Costs,51300,
159,MEATS,BACON SHINGLE 10/12 HY GF PR12,Beef/Pork Costs,51110, 159,MEATS,BACON SHINGLE 10/12 HY GF PR12,Beef/Pork Costs,51110,
@@ -194,7 +194,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
192,PAPER & DISP,BOWL PAPER MOLDED FIBER 32OZ,Paper Costs,55000, 192,PAPER & DISP,BOWL PAPER MOLDED FIBER 32OZ,Paper Costs,55000,
193,PAPER & DISP,BOX CORR CATER #1 LOGO 2021,Paper Costs,55000, 193,PAPER & DISP,BOX CORR CATER #1 LOGO 2021,Paper Costs,55000,
194,DISPENSER BEVRG,SYRUP LEMONADE BIB,Soft Beverage Costs,52000, 194,DISPENSER BEVRG,SYRUP LEMONADE BIB,Soft Beverage Costs,52000,
195,PAPER & DISP,FOIL ALMN ROLL HVY WGT 500 FT,Paper Costs,55000, 195,PAPER & DISP,FOIL ALMN ROLL HVY WGT 500 FT,Dry Goods Costs,51500,
196,DAIRY PRODUCTS,CHEESE FETA CHUNKS IN BRNE,Dairy Costs,51300, 196,DAIRY PRODUCTS,CHEESE FETA CHUNKS IN BRNE,Dairy Costs,51300,
197,POULTRY,CHICKEN CVP WOG WHL HAL,Chicken/ Poultry Costs,51120, 197,POULTRY,CHICKEN CVP WOG WHL HAL,Chicken/ Poultry Costs,51120,
198,PAPER & DISP,CONTAINER MFPP 1C HNG 9X6 WHT,Paper Costs,55000, 198,PAPER & DISP,CONTAINER MFPP 1C HNG 9X6 WHT,Paper Costs,55000,
@@ -203,11 +203,11 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
201,DISPENSER BEVRG,SYRUP LEMON LIME BIB,Soft Beverage Costs,52000, 201,DISPENSER BEVRG,SYRUP LEMON LIME BIB,Soft Beverage Costs,52000,
202,DAIRY PRODUCTS,CHEESE FETA PAIL,Dairy Costs,51300, 202,DAIRY PRODUCTS,CHEESE FETA PAIL,Dairy Costs,51300,
203,CANNED AND DRY,CHGS FOR MINIMUM ORDER,Food Costs,50000, 203,CANNED AND DRY,CHGS FOR MINIMUM ORDER,Food Costs,50000,
204,CANNED AND DRY,BREAD CRUMB PLAIN MED,Food Costs,50000, 204,CANNED AND DRY,BREAD CRUMB PLAIN MED,Dry Goods Costs,51500,
205,CANNED AND DRY,DRESSING SALAD PRASINI,Food Costs,50000, 205,CANNED AND DRY,DRESSING SALAD PRASINI,Dressing & Sauce Cost,51450,
206,CANNED AND DRY,OIL CORN,Food Costs,50000, 206,CANNED AND DRY,OIL CORN,Dressing & Sauce Cost,51450,
207,CANNED AND DRY,OLIVE KALAMATA PTD BRNE 22 LB,Food Costs,50000, 207,CANNED AND DRY,OLIVE KALAMATA PTD BRNE 22 LB,Produce Costs,51200,
208,CANNED AND DRY,SPICE TURMERIC GROUND,Food Costs,50000, 208,CANNED AND DRY,SPICE TURMERIC GROUND,Dry Goods Costs,51500,
209,PRODUCE,SQUASH ZUCCHINI MEDIUM FRESH,Produce Costs,51200, 209,PRODUCE,SQUASH ZUCCHINI MEDIUM FRESH,Produce Costs,51200,
210,PRODUCE,ONION GREEN ICELS ROOTLESS,Produce Costs,51200, 210,PRODUCE,ONION GREEN ICELS ROOTLESS,Produce Costs,51200,
211,CANNED AND DRY,SODA COLA PEPSI ZERO,Soft Beverage Costs,52000, 211,CANNED AND DRY,SODA COLA PEPSI ZERO,Soft Beverage Costs,52000,
@@ -219,7 +219,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
217,FROZEN,BAKLAVA GREEK PASTRY,Food Costs,50000, 217,FROZEN,BAKLAVA GREEK PASTRY,Food Costs,50000,
218,DISPENSER BEVRG,SYRUP DR PPR DIET BIB,Soft Beverage Costs,52000, 218,DISPENSER BEVRG,SYRUP DR PPR DIET BIB,Soft Beverage Costs,52000,
219,DISPENSER BEVRG,SYRUP MOUNTAIN DEW BIB,Soft Beverage Costs,52000, 219,DISPENSER BEVRG,SYRUP MOUNTAIN DEW BIB,Soft Beverage Costs,52000,
220,CANNED AND DRY,SAUCE CHILI HOT SRIRACHA,Food Costs,50000, 220,CANNED AND DRY,SAUCE CHILI HOT SRIRACHA,Dry Goods Costs,51500,
221,PAPER & DISP,SKEWER BAMBOO 10IN,Paper Costs,55000, 221,PAPER & DISP,SKEWER BAMBOO 10IN,Paper Costs,55000,
222,CANNED AND DRY,RICE BASMATI,Food Costs,50000, 222,CANNED AND DRY,RICE BASMATI,Food Costs,50000,
223,PAPER & DISP,WRAP PAPER 14X14 LOGO,Paper Costs,55000, 223,PAPER & DISP,WRAP PAPER 14X14 LOGO,Paper Costs,55000,
@@ -227,12 +227,12 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
225,MEATS,PORK BUTT BNLS VP PR12,Beef/Pork Costs,51110, 225,MEATS,PORK BUTT BNLS VP PR12,Beef/Pork Costs,51110,
226,DAIRY PRODUCTS,YOGURT PLAIN GREEK NONFAT,Dairy Costs,51300, 226,DAIRY PRODUCTS,YOGURT PLAIN GREEK NONFAT,Dairy Costs,51300,
227,PAPER & DISP,TONG PLAS 9 BLK SNAP N SERVE,Paper Costs,55000, 227,PAPER & DISP,TONG PLAS 9 BLK SNAP N SERVE,Paper Costs,55000,
228,CANNED AND DRY,SAUCE HOT SRIRACHA,Food Costs,50000, 228,CANNED AND DRY,SAUCE HOT SRIRACHA,Dry Goods Costs,51500,
229,DISPENSER BEVRG,SYRUP DR PEPPER BIB,Soft Beverage Costs,52000, 229,DISPENSER BEVRG,SYRUP DR PEPPER BIB,Soft Beverage Costs,52000,
230,HLTHCAR/HOSPITALITY,BILLING MISC REGULAR,Food Costs,50000, 230,HLTHCAR/HOSPITALITY,BILLING MISC REGULAR,Food Costs,50000,
231,PAPER & DISP,FORK PLAS BLK MEDHVY MDLNGTH,Paper Costs,55000, 231,PAPER & DISP,FORK PLAS BLK MEDHVY MDLNGTH,Paper Costs,55000,
232,DAIRY PRODUCTS,YOGURT PLAIN ORIGINAL FTFR,Dairy Costs,51300, 232,DAIRY PRODUCTS,YOGURT PLAIN ORIGINAL FTFR,Dairy Costs,51300,
233,SUPP & EQUIP,MOP HEAD BLND LPD ALL PURP LRG,Food Costs,50000, 233,SUPP & EQUIP,MOP HEAD BLND LPD ALL PURP LRG,Cleaning Supplies,74100,
234,PAPER & DISP,SPOON PLAS WHT MEDHVY MDLNGTH,Paper Costs,55000, 234,PAPER & DISP,SPOON PLAS WHT MEDHVY MDLNGTH,Paper Costs,55000,
235,MEATS,BEEF GROUND BULK NAT 80/20,Beef/Pork Costs,51110, 235,MEATS,BEEF GROUND BULK NAT 80/20,Beef/Pork Costs,51110,
236,PAPER & DISP,CONTAINER PAPER HNG 9X6 PFF,Paper Costs,55000, 236,PAPER & DISP,CONTAINER PAPER HNG 9X6 PFF,Paper Costs,55000,
@@ -245,9 +245,9 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
243,PRODUCE,CUCUMBER ENGLISH FRSH,Produce Costs,51200, 243,PRODUCE,CUCUMBER ENGLISH FRSH,Produce Costs,51200,
244,PAPER & DISP,FORK PLAS WHT MED HVY MDLNGTH,Paper Costs,55000, 244,PAPER & DISP,FORK PLAS WHT MED HVY MDLNGTH,Paper Costs,55000,
245,PAPER & DISP,CUP PLAS 12-14OZ CLR STRT WALL,Paper Costs,55000, 245,PAPER & DISP,CUP PLAS 12-14OZ CLR STRT WALL,Paper Costs,55000,
246,CANNED AND DRY,SAUCE HOT BOTTLE,Food Costs,50000, 246,CANNED AND DRY,SAUCE HOT BOTTLE,Dry Goods Costs,51500,
247,CANNED AND DRY,OIL OLIVE BLEND 80/20,Food Costs,50000, 247,CANNED AND DRY,OIL OLIVE BLEND 80/20,Dry Goods Costs,51500,
248,CANNED AND DRY,SPICE OREGANO LEAF RUBBED,Food Costs,50000, 248,CANNED AND DRY,SPICE OREGANO LEAF RUBBED,Dry Goods Costs,51500,
249,DAIRY PRODUCTS,YOGURT VANILLA GREEK NFAT,Dairy Costs,51300, 249,DAIRY PRODUCTS,YOGURT VANILLA GREEK NFAT,Dairy Costs,51300,
250,FROZEN,BUN BRIOCHE HOMESTYLE 4,Food Costs,50000, 250,FROZEN,BUN BRIOCHE HOMESTYLE 4,Food Costs,50000,
251,CANNED AND DRY,WATER SPRKLG IMPRTD MNERAL GLS,Food Costs,50000, 251,CANNED AND DRY,WATER SPRKLG IMPRTD MNERAL GLS,Food Costs,50000,
@@ -260,18 +260,18 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
258,CANNED AND DRY,PEPPER GREEN CHILI WHL,Food Costs,50000, 258,CANNED AND DRY,PEPPER GREEN CHILI WHL,Food Costs,50000,
259,CANNED AND DRY,PEPPER JALAPENO SLI FIELD RUN,Food Costs,50000, 259,CANNED AND DRY,PEPPER JALAPENO SLI FIELD RUN,Food Costs,50000,
260,CANNED AND DRY,SAUCE MIX HOLLANDAISE GF,Food Costs,50000, 260,CANNED AND DRY,SAUCE MIX HOLLANDAISE GF,Food Costs,50000,
261,CANNED AND DRY,VINEGAR DISTILLED WHITE 5%,Food Costs,50000, 261,CANNED AND DRY,VINEGAR DISTILLED WHITE 5%,Dry Goods Costs,51500,
262,PAPER & DISP,COVER TOILET SEAT,Paper Costs,55000, 262,PAPER & DISP,COVER TOILET SEAT,Paper Costs,55000,
263,PAPER & DISP,FILM PVC 2000FT ROLL,Paper Costs,55000, 263,PAPER & DISP,FILM PVC 2000FT ROLL,Paper Costs,55000,
264,PAPER & DISP,DOILY PAPER NRMDY LACE 6,Paper Costs,55000, 264,PAPER & DISP,DOILY PAPER NRMDY LACE 6,Paper Costs,55000,
265,PAPER & DISP,KIT CUTLERY MED PP KFS S&P NAP,Paper Costs,55000, 265,PAPER & DISP,KIT CUTLERY MED PP KFS S&P NAP,Paper Costs,55000,
266,PAPER & DISP,NAPKIN DNR 2P 15X16.25 1/8F WH,Paper Costs,55000, 266,PAPER & DISP,NAPKIN DNR 2P 15X16.25 1/8F WH,Paper Costs,55000,
267,CHEMICAL/JANTRL,DETERGENT POT/PAN LIQ PINK RTU,Food Costs,50000, 267,CHEMICAL/JANTRL,DETERGENT POT/PAN LIQ PINK RTU,Cleaning Supplies,74100,
268,CHEMICAL/JANTRL,SALT GRANULE SOLAR WATER SOFT,Food Costs,50000, 268,CHEMICAL/JANTRL,SALT GRANULE SOLAR WATER SOFT,Food Costs,50000,
269,PRODUCE,CARROT FRESH JUMBO,Produce Costs,51200, 269,PRODUCE,CARROT FRESH JUMBO,Produce Costs,51200,
270,PRODUCE,LIME FRESH 200CT,Produce Costs,51200, 270,PRODUCE,LIME FRESH 200CT,Produce Costs,51200,
271,PAPER & DISP,CUP PLAS RPET CLR 16 OZ,Paper Costs,55000, 271,PAPER & DISP,CUP PLAS RPET CLR 16 OZ,Paper Costs,55000,
272,CANNED AND DRY,SAUCE HOT,Food Costs,50000, 272,CANNED AND DRY,SAUCE HOT,Dry Goods Costs,51500,
273,MEATS,BACON SHINGLE 10/12 AW GF PR12,Beef/Pork Costs,51110, 273,MEATS,BACON SHINGLE 10/12 AW GF PR12,Beef/Pork Costs,51110,
274,PAPER & DISP,CRAYON RED BLUE YEL GREEN,Paper Costs,55000, 274,PAPER & DISP,CRAYON RED BLUE YEL GREEN,Paper Costs,55000,
275,DAIRY PRODUCTS,CREAMER HALF AND HALF PC ASEP,Dairy Costs,51300, 275,DAIRY PRODUCTS,CREAMER HALF AND HALF PC ASEP,Dairy Costs,51300,
@@ -282,7 +282,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
280,CANNED AND DRY,KETCHUP SQUEEZE UPSIDE DOWN,Food Costs,50000, 280,CANNED AND DRY,KETCHUP SQUEEZE UPSIDE DOWN,Food Costs,50000,
281,PAPER & DISP,GLOVE NITRILE FDSRV PF BLK LRG,Paper Costs,55000, 281,PAPER & DISP,GLOVE NITRILE FDSRV PF BLK LRG,Paper Costs,55000,
282,MEATS,BEEF GRND CHUCK 81/19 CHUB FRS,Beef/Pork Costs,51110, 282,MEATS,BEEF GRND CHUCK 81/19 CHUB FRS,Beef/Pork Costs,51110,
283,PAPER & DISP,LID FOIL F/FULL STM TBL PAN,Paper Costs,55000, 283,PAPER & DISP,LID FOIL F/FULL STM TBL PAN,Dry Goods Costs,51500,
284,PAPER & DISP,FORK WOODEN DISP,Paper Costs,55000, 284,PAPER & DISP,FORK WOODEN DISP,Paper Costs,55000,
285,PAPER & DISP,SKEWER BAMBOO THIN 8 IN,Paper Costs,55000, 285,PAPER & DISP,SKEWER BAMBOO THIN 8 IN,Paper Costs,55000,
286,PAPER & DISP,WRAP DELI WHT 12X12 GRS RESIST,Paper Costs,55000, 286,PAPER & DISP,WRAP DELI WHT 12X12 GRS RESIST,Paper Costs,55000,
@@ -292,9 +292,9 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
290,PAPER & DISP,TRAY FOOD PAPER #2 LOGO,Paper Costs,55000, 290,PAPER & DISP,TRAY FOOD PAPER #2 LOGO,Paper Costs,55000,
291,PAPER & DISP,LID PLAS CLR F/1.5-2.5OZ PRTN,Paper Costs,55000, 291,PAPER & DISP,LID PLAS CLR F/1.5-2.5OZ PRTN,Paper Costs,55000,
292,DISPENSER BEVRG,SYRUP TEA RASP 5X1 BRISK,Soft Beverage Costs,52000, 292,DISPENSER BEVRG,SYRUP TEA RASP 5X1 BRISK,Soft Beverage Costs,52000,
293,PAPER & DISP,PAN FOIL STM TBL FULL DP 3-3/8,Paper Costs,55000, 293,PAPER & DISP,PAN FOIL STM TBL FULL DP 3-3/8,Dry Goods Costs,51500,
294,DISPENSER BEVRG,SYRUP ROOT BEER BIB,Soft Beverage Costs,52000, 294,DISPENSER BEVRG,SYRUP ROOT BEER BIB,Soft Beverage Costs,52000,
295,PAPER & DISP,FOIL ALMN ROLL HVY WGT 1000 FT,Paper Costs,55000, 295,PAPER & DISP,FOIL ALMN ROLL HVY WGT 1000 FT,Dry Goods Costs,51500,
296,DAIRY PRODUCTS,CHEESE GORGONZOLA WHEEL HALF,Dairy Costs,51300, 296,DAIRY PRODUCTS,CHEESE GORGONZOLA WHEEL HALF,Dairy Costs,51300,
297,DAIRY PRODUCTS,CHEESE MOZZ WM SHRED GOLD PREM,Dairy Costs,51300, 297,DAIRY PRODUCTS,CHEESE MOZZ WM SHRED GOLD PREM,Dairy Costs,51300,
298,DAIRY PRODUCTS,CREAM SOUR CULTRD GRADE A,Dairy Costs,51300, 298,DAIRY PRODUCTS,CREAM SOUR CULTRD GRADE A,Dairy Costs,51300,
@@ -311,7 +311,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
309,SEAFOOD,SCALLOP SEA WTR ADD 10/20 USA,Seafood Costs,51130, 309,SEAFOOD,SCALLOP SEA WTR ADD 10/20 USA,Seafood Costs,51130,
310,MEATS,BACON SLAB SLI 13/17 CT PR12,Beef/Pork Costs,51110, 310,MEATS,BACON SLAB SLI 13/17 CT PR12,Beef/Pork Costs,51110,
311,CANNED AND DRY,DRESSING MIX RANCH,Food Costs,50000, 311,CANNED AND DRY,DRESSING MIX RANCH,Food Costs,50000,
312,FROZEN,BUN BRIOCHE HOMESTYLE 4.25,Food Costs,50000, 312,FROZEN,BUN BRIOCHE HOMESTYLE 4.25,Bread and Bun Costs,51400,
313,CANNED AND DRY,SODA LEMON LIME,Soft Beverage Costs,52000, 313,CANNED AND DRY,SODA LEMON LIME,Soft Beverage Costs,52000,
314,FROZEN,PUREE ORANGE BLOOD CONCENTRATE,Food Costs,50000, 314,FROZEN,PUREE ORANGE BLOOD CONCENTRATE,Food Costs,50000,
315,CANNED AND DRY,FLOUR HI-GLUTEN BL EN MT AA,Dry Good Costs,51500, 315,CANNED AND DRY,FLOUR HI-GLUTEN BL EN MT AA,Dry Good Costs,51500,
@@ -327,23 +327,23 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
325,DAIRY PRODUCTS,CHEESE FETA CHUNKS PAIL PREM,Dairy Costs,51300, 325,DAIRY PRODUCTS,CHEESE FETA CHUNKS PAIL PREM,Dairy Costs,51300,
326,PRODUCE,PEPPER GREEN BELL LARGE FRESH,Produce Costs,51200, 326,PRODUCE,PEPPER GREEN BELL LARGE FRESH,Produce Costs,51200,
327,PRODUCE,TOMATO ROMA FRSH,Produce Costs,51200, 327,PRODUCE,TOMATO ROMA FRSH,Produce Costs,51200,
328,CHEMICAL/JANTRL,CLEANER DEGREASER OVEN RTU,Food Costs,50000, 328,CHEMICAL/JANTRL,CLEANER DEGREASER OVEN RTU,Cleaning Supplies,74100,
329,PAPER & DISP,BOX CORR CATER #4 LOGO 2021,Paper Costs,55000, 329,PAPER & DISP,BOX CORR CATER #4 LOGO 2021,Paper Costs,55000,
330,PAPER & DISP,LID PLAS HI DOME DESSRT,Paper Costs,55000, 330,PAPER & DISP,LID PLAS HI DOME DESSRT,Paper Costs,55000,
331,MEATS,PORK BUTT BNLS 1/4 6-9#EA,Beef/Pork Costs,51110, 331,MEATS,PORK BUTT BNLS 1/4 6-9#EA,Beef/Pork Costs,51110,
332,PAPER & DISP,CUP PAPER HOT WHT TALL 12OZ,Paper Costs,55000, 332,PAPER & DISP,CUP PAPER HOT WHT TALL 12OZ,Paper Costs,55000,
333,PAPER & DISP,SPOON PLAS SOUP BLACK XHEAVY,Paper Costs,55000, 333,PAPER & DISP,SPOON PLAS SOUP BLACK XHEAVY,Paper Costs,55000,
334,PAPER & DISP,FOIL ALMN ROLL STD WGT 1000 FT,Paper Costs,55000, 334,PAPER & DISP,FOIL ALMN ROLL STD WGT 1000 FT,Dry Goods Costs,51500,
335,SUPP & EQUIP,PAD SCRUB STNLS 50GR 1.75OZ,Food Costs,50000, 335,SUPP & EQUIP,PAD SCRUB STNLS 50GR 1.75OZ,Cleaning Supplies,74100,
336,DISPENSER BEVRG,SYRUP TEA UNSWTD 5X1,Soft Beverage Costs,52000, 336,DISPENSER BEVRG,SYRUP TEA UNSWTD 5X1,Soft Beverage Costs,52000,
337,FROZEN,BUN BRIOCHE SPLIT TOP 4IN SLI,Food Costs,50000, 337,FROZEN,BUN BRIOCHE SPLIT TOP 4IN SLI,Bread and Bun Costs,51400,
338,CANNED AND DRY,WATER MINERAL LIMONATA CAN,Beverages Costs,52000, 338,CANNED AND DRY,WATER MINERAL LIMONATA CAN,Beverages Costs,52000,
339,PRODUCE,GARLIC PEELED CHINESE,Produce Costs,51200, 339,PRODUCE,GARLIC PEELED CHINESE,Produce Costs,51200,
340,PAPER & DISP,LID PLAS FLAT F/12-24Z PET CUP,Paper Costs,55000, 340,PAPER & DISP,LID PLAS FLAT F/12-24Z PET CUP,Paper Costs,55000,
341,FROZEN,BILLING MISC FROZEN,Food Costs,50000, 341,FROZEN,BILLING MISC FROZEN,Food Costs,50000,
342,PAPER & DISP,KNIFE PLAS BLK PLA COMPSTABLE,Paper Costs,55000, 342,PAPER & DISP,KNIFE PLAS BLK PLA COMPSTABLE,Paper Costs,55000,
343,PAPER & DISP,GLOVE NITRILE MED,Paper Costs,55000, 343,PAPER & DISP,GLOVE NITRILE MED,Paper Costs,55000,
344,SUPP & EQUIP,GRILL BRICK 3.5IN THICK,Food Costs,50000, 344,SUPP & EQUIP,GRILL BRICK 3.5IN THICK,Cleaning Supplies,74100,
345,PRODUCE,CABBAGE SAVOY FRSH,Produce Costs,51200, 345,PRODUCE,CABBAGE SAVOY FRSH,Produce Costs,51200,
346,PRODUCE,FLOWER ORCHID MULTI COLORED,Produce Costs,51200, 346,PRODUCE,FLOWER ORCHID MULTI COLORED,Produce Costs,51200,
347,CANNED AND DRY,OIL AVOCADO,Food Costs,50000, 347,CANNED AND DRY,OIL AVOCADO,Food Costs,50000,
@@ -393,7 +393,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
391,PAPER & DISP,TOWEL MULTIFOLD 9.4X9.2 WHT 1P,Paper Costs,55000, 391,PAPER & DISP,TOWEL MULTIFOLD 9.4X9.2 WHT 1P,Paper Costs,55000,
392,PAPER & DISP,TISSUE TOILET WRPD 4X3.8 2PLY,Paper Costs,55000, 392,PAPER & DISP,TISSUE TOILET WRPD 4X3.8 2PLY,Paper Costs,55000,
393,MEATS,BEEF GRND 80/20 BULK,Beef/Pork Costs,51110, 393,MEATS,BEEF GRND 80/20 BULK,Beef/Pork Costs,51110,
394,CANNED AND DRY,MAYONNAISE HEAVY DUTY,Food Costs,50000, 394,CANNED AND DRY,MAYONNAISE HEAVY DUTY,Dressing & Sauce Cost,51450,
395,PAPER & DISP,CONTAINER PLAS HNG 9X6 WHT,Paper Costs,55000, 395,PAPER & DISP,CONTAINER PLAS HNG 9X6 WHT,Paper Costs,55000,
396,PAPER & DISP,SPOON PLASTIC BLK BAGGED,Paper Costs,55000, 396,PAPER & DISP,SPOON PLASTIC BLK BAGGED,Paper Costs,55000,
397,PAPER & DISP,FORK PLAS BLK HVY FULL LNGTH,Paper Costs,55000, 397,PAPER & DISP,FORK PLAS BLK HVY FULL LNGTH,Paper Costs,55000,
@@ -406,7 +406,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
404,CANNED AND DRY,BEAN BLACK,Food Costs,50000, 404,CANNED AND DRY,BEAN BLACK,Food Costs,50000,
405,CANNED AND DRY,SYRUP CHOCOLATE PLAS JUG,Food Costs,50000, 405,CANNED AND DRY,SYRUP CHOCOLATE PLAS JUG,Food Costs,50000,
406,CANNED AND DRY,TUNA LIGHT SKIPJACK CHUNK WTR,Food Costs,50000, 406,CANNED AND DRY,TUNA LIGHT SKIPJACK CHUNK WTR,Food Costs,50000,
407,CHEMICAL/JANTRL,DEGREASER HEAVY DUTY RTU,Food Costs,50000, 407,CHEMICAL/JANTRL,DEGREASER HEAVY DUTY RTU,Cleaning Supplies,74100,
408,CANNED AND DRY,JAM BLACKBERRY CUP,Food Costs,50000, 408,CANNED AND DRY,JAM BLACKBERRY CUP,Food Costs,50000,
409,CANNED AND DRY,PICKLE WHL DILL KO REF 75/85,Food Costs,50000, 409,CANNED AND DRY,PICKLE WHL DILL KO REF 75/85,Food Costs,50000,
410,DAIRY PRODUCTS,CHEESE QUESO FRESCO CASERO,Dairy Costs,51300, 410,DAIRY PRODUCTS,CHEESE QUESO FRESCO CASERO,Dairy Costs,51300,
@@ -423,7 +423,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
421,PAPER & DISP,CONTAINER PAPER HNG 9X6 FIB,Paper Costs,55000, 421,PAPER & DISP,CONTAINER PAPER HNG 9X6 FIB,Paper Costs,55000,
422,PAPER & DISP,LINER REPRO 40X46 1.5 ML BLK,Paper Costs,55000, 422,PAPER & DISP,LINER REPRO 40X46 1.5 ML BLK,Paper Costs,55000,
423,PAPER & DISP,TAPE PAPR REG THERMAL 3-1/8,Paper Costs,55000, 423,PAPER & DISP,TAPE PAPR REG THERMAL 3-1/8,Paper Costs,55000,
424,SUPP & EQUIP,PAD SCOUR GRN 6X9IN ANTIMICRO,Food Costs,50000, 424,SUPP & EQUIP,PAD SCOUR GRN 6X9IN ANTIMICRO,Cleaning Supplies,74100,
425,PAPER & DISP,GLOVE NITRILE BLUE XL,Paper Costs,55000, 425,PAPER & DISP,GLOVE NITRILE BLUE XL,Paper Costs,55000,
426,PRODUCE,CUCUMBER ENGLISH LONG,Produce Costs,51200, 426,PRODUCE,CUCUMBER ENGLISH LONG,Produce Costs,51200,
427,PRODUCE,TOMATO ROMA MED,Produce Costs,51200, 427,PRODUCE,TOMATO ROMA MED,Produce Costs,51200,
@@ -434,13 +434,13 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
432,POULTRY,CHICKEN CVP THIGH B/S HALAL JM,Chicken/ Poultry Costs,51120, 432,POULTRY,CHICKEN CVP THIGH B/S HALAL JM,Chicken/ Poultry Costs,51120,
433,CANNED AND DRY,DRESSING HONEY MUSTARD,Food Costs,50000, 433,CANNED AND DRY,DRESSING HONEY MUSTARD,Food Costs,50000,
434,PRODUCE,JUICE LEMON PSTRZD ULTRA PREM,Produce Costs,51200, 434,PRODUCE,JUICE LEMON PSTRZD ULTRA PREM,Produce Costs,51200,
435,CANNED AND DRY,SPICE PAPRIKA GROUND,Food Costs,50000, 435,CANNED AND DRY,SPICE PAPRIKA GROUND,Dry Goods Costs,51500,
436,PAPER & DISP,FORK PLAS WHT HVY FULL LENGTH,Paper Costs,55000, 436,PAPER & DISP,FORK PLAS WHT HVY FULL LENGTH,Paper Costs,55000,
437,PAPER & DISP,LID FOIL F/ HALF STMTBL PAN,Paper Costs,55000, 437,PAPER & DISP,LID FOIL F/ HALF STMTBL PAN,Dry Goods Costs,51500,
438,SUPP & EQUIP,PAN FOIL HALF DEEP 100CT,Food Costs,50000, 438,SUPP & EQUIP,PAN FOIL HALF DEEP 100CT,Food Costs,50000,
439,PAPER & DISP,CONTAINER FOAM HNG LRG 1C,Paper Costs,55000, 439,PAPER & DISP,CONTAINER FOAM HNG LRG 1C,Paper Costs,55000,
440,PAPER & DISP,LINER TRASH 40X48 13 MC NAT,Paper Costs,55000, 440,PAPER & DISP,LINER TRASH 40X48 13 MC NAT,Paper Costs,55000,
441,CANNED AND DRY,KETCHUP FCY,Food Costs,50000, 441,CANNED AND DRY,KETCHUP FCY,Dry Goods Costs,51500,
442,PAPER & DISP,STRAW PLAS WRPD FLEX WHT 7.625,Paper Costs,55000, 442,PAPER & DISP,STRAW PLAS WRPD FLEX WHT 7.625,Paper Costs,55000,
443,CANNED AND DRY,WATER SPRKLG CHRY/POMGRNT,Food Costs,50000, 443,CANNED AND DRY,WATER SPRKLG CHRY/POMGRNT,Food Costs,50000,
444,PAPER & DISP,APRON POLY EMBSD WHT 28X46 ECO,Paper Costs,55000, 444,PAPER & DISP,APRON POLY EMBSD WHT 28X46 ECO,Paper Costs,55000,
@@ -457,13 +457,13 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
455,SEAFOOD,CALAMARI TUBE & TNT 5-8 INCH,Seafood Costs,51130, 455,SEAFOOD,CALAMARI TUBE & TNT 5-8 INCH,Seafood Costs,51130,
456,SEAFOOD,SHRIMP CKD & PLD BAY 90\150 FZ,Seafood Costs,51130, 456,SEAFOOD,SHRIMP CKD & PLD BAY 90\150 FZ,Seafood Costs,51130,
457,SEAFOOD,SHRIMP WHT GH 16-20,Seafood Costs,51130, 457,SEAFOOD,SHRIMP WHT GH 16-20,Seafood Costs,51130,
458,SUPP & EQUIP,BROOM ANGULAR FLAGGED,Food Costs,50000, 458,SUPP & EQUIP,BROOM ANGULAR FLAGGED,Cleaning Supplies,74100,
459,CANNED AND DRY,SAUCE CHILI SRIRACHA,Food Costs,50000, 459,CANNED AND DRY,SAUCE CHILI SRIRACHA,Food Costs,50000,
460,PAPER & DISP,CONTAINER PAPER #1 TK OUT KRFT,Paper Costs,55000, 460,PAPER & DISP,CONTAINER PAPER #1 TK OUT KRFT,Paper Costs,55000,
461,PAPER & DISP,SPOON PLAS BLK MEDHVY MDLNGTH,Paper Costs,55000, 461,PAPER & DISP,SPOON PLAS BLK MEDHVY MDLNGTH,Paper Costs,55000,
462,PAPER & DISP,LID PLAS F/ 12/16/21/24 CUPS,Paper Costs,55000, 462,PAPER & DISP,LID PLAS F/ 12/16/21/24 CUPS,Paper Costs,55000,
463,PAPER & DISP,GLOVE NITRILE FDSRV PF BLU XL,Paper Costs,55000, 463,PAPER & DISP,GLOVE NITRILE FDSRV PF BLU XL,Paper Costs,55000,
464,CANNED AND DRY,SODA ORANGE,Food Costs,50000, 464,CANNED AND DRY,SODA ORANGE,Soft Beverage Cost,52000,
465,PRODUCE,SQUASH ZUCCHINI MED FRSH,Produce Costs,51200, 465,PRODUCE,SQUASH ZUCCHINI MED FRSH,Produce Costs,51200,
466,CANNED AND DRY,SPICE TURMERIC GRND ORGANIC,Food Costs,50000, 466,CANNED AND DRY,SPICE TURMERIC GRND ORGANIC,Food Costs,50000,
467,CANNED AND DRY,WATER SPRING IMPORTED GLS,Food Costs,50000, 467,CANNED AND DRY,WATER SPRING IMPORTED GLS,Food Costs,50000,
@@ -474,9 +474,9 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
472,PAPER & DISP,GLOVE NITRILE BLK PEDRFREE LRG,Paper Costs,55000, 472,PAPER & DISP,GLOVE NITRILE BLK PEDRFREE LRG,Paper Costs,55000,
473,PAPER & DISP,TRAY CARRYOUT 4CUP,Paper Costs,55000, 473,PAPER & DISP,TRAY CARRYOUT 4CUP,Paper Costs,55000,
474,PAPER & DISP,BAG PLAS T-SHRT THNKYOU12X7X22,Paper Costs,55000, 474,PAPER & DISP,BAG PLAS T-SHRT THNKYOU12X7X22,Paper Costs,55000,
475,PAPER & DISP,GRILL BRICK 3.5IN THICK,Paper Costs,55000, 475,PAPER & DISP,GRILL BRICK 3.5IN THICK,Cleaning Supplies,74100,
476,PAPER & DISP,PAD SCOUR GRN 6X9IN ANTIMICRO,Paper Costs,55000, 476,PAPER & DISP,PAD SCOUR GRN 6X9IN ANTIMICRO,Cleaning Supplies,74100,
477,PAPER & DISP,PAD SCRUB STNLS 50GR 1.75OZ,Paper Costs,55000, 477,PAPER & DISP,PAD SCRUB STNLS 50GR 1.75OZ,Cleaning Supplies,74100,
478,PAPER & DISP,LID TOGO PLAS F/12-16-32 OZ,Paper Costs,55000, 478,PAPER & DISP,LID TOGO PLAS F/12-16-32 OZ,Paper Costs,55000,
479,PRODUCE,PEPPER GREEN BELL FRSH LG,Produce Costs,51200, 479,PRODUCE,PEPPER GREEN BELL FRSH LG,Produce Costs,51200,
480,DISPENSER BEVRG,SYRUP COLA WILD CHERRY,Soft Beverage Costs,52000, 480,DISPENSER BEVRG,SYRUP COLA WILD CHERRY,Soft Beverage Costs,52000,
@@ -486,25 +486,25 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
484,PRODUCE,TOMATO ROMA FRESH,Produce Costs,51200, 484,PRODUCE,TOMATO ROMA FRESH,Produce Costs,51200,
485,DAIRY PRODUCTS,YOGURT PLAIN GREEK WM 4% FAT,Dairy Costs,51300, 485,DAIRY PRODUCTS,YOGURT PLAIN GREEK WM 4% FAT,Dairy Costs,51300,
486,CHEMICAL/JANTRL,CLEANER OVEN GREASESTRIP+ NP,Food Costs,50000, 486,CHEMICAL/JANTRL,CLEANER OVEN GREASESTRIP+ NP,Food Costs,50000,
487,FROZEN,BUN BRIOCHE SLI 4.5,Food Costs,50000, 487,FROZEN,BUN BRIOCHE SLI 4.5,Bread and Bun Costs,51400,
488,PRODUCE,LEMON FRESH,Produce Costs,51200, 488,PRODUCE,LEMON FRESH,Produce Costs,51200,
489,SUPP & EQUIP,DISH SUPREME GLASS,Food Costs,50000, 489,SUPP & EQUIP,DISH SUPREME GLASS,Food Costs,50000,
490,PAPER & DISP,CONTAINER POLYETHELYN,Paper Costs,55000, 490,PAPER & DISP,CONTAINER POLYETHELYN,Paper Costs,55000,
491,PAPER & DISP,FILTER GREASE CONE 10 IN,Paper Costs,55000, 491,PAPER & DISP,FILTER GREASE CONE 10 IN,Cleaning Supplies,74100,
492,PRODUCE,SQUASH ZUCCHINI FCY FRESH,Produce Costs,51200, 492,PRODUCE,SQUASH ZUCCHINI FCY FRESH,Produce Costs,51200,
493,CHEMICAL/JANTRL,CLEANER ALL PURPOSE PINE RTU,Food Costs,50000, 493,CHEMICAL/JANTRL,CLEANER ALL PURPOSE PINE RTU,Cleaning Supplies,74100,
494,CANNED AND DRY,KETCHUP PACKET FCY FOIL,Food Costs,50000, 494,CANNED AND DRY,KETCHUP PACKET FCY FOIL,Dry Goods Costs,51500,
495,PRODUCE,MUSHROOM PORTABELLA CP LRG FSH,Produce Costs,51200, 495,PRODUCE,MUSHROOM PORTABELLA CP LRG FSH,Produce Costs,51200,
496,PAPER & DISP,CONTAINER MINERAL 9X6 HNG 1CPT,Paper Costs,55000, 496,PAPER & DISP,CONTAINER MINERAL 9X6 HNG 1CPT,Paper Costs,55000,
497,PAPER & DISP,WRAP DRY WAX DELI HVY 10X10.75,Paper Costs,55000, 497,PAPER & DISP,WRAP DRY WAX DELI HVY 10X10.75,Paper Costs,55000,
498,PAPER & DISP,LID PLAS 12/16/22 OZ CUP,Paper Costs,55000, 498,PAPER & DISP,LID PLAS 12/16/22 OZ CUP,Paper Costs,55000,
499,CHEMICAL/JANTRL,POLISH S-S SATIN SHINE ARSL,Food Costs,50000, 499,CHEMICAL/JANTRL,POLISH S-S SATIN SHINE ARSL,Cleaning Supplies,74100,
500,CANNED AND DRY,SPICE MARJORAM LVS,Food Costs,50000, 500,CANNED AND DRY,SPICE MARJORAM LVS,Dry Goods Costs,51500,
501,SUPP & EQUIP,BOTTLE PLASTIC SQUEEZE WIDEMTH,Food Costs,50000, 501,SUPP & EQUIP,BOTTLE PLASTIC SQUEEZE WIDEMTH,Paperware Cost,55000,
502,PAPER & DISP,PAN FOIL STM TBL DEEPXH 2-9/16,Paper Costs,55000, 502,PAPER & DISP,PAN FOIL STM TBL DEEPXH 2-9/16,Dry Goods Costs,51500,
503,PAPER & DISP,TONG PLAS BLK 6.25IN SM SRVING,Paper Costs,55000, 503,PAPER & DISP,TONG PLAS BLK 6.25IN SM SRVING,Paper Costs,55000,
504,PAPER & DISP,FORK PLAS WHT P/P,Paper Costs,55000, 504,PAPER & DISP,FORK PLAS WHT P/P,Paper Costs,55000,
505,CHEMICAL/JANTRL,CLEANER DEGRSR HGH TMP GRL RTU,Food Costs,50000, 505,CHEMICAL/JANTRL,CLEANER DEGRSR HGH TMP GRL RTU,Cleaning Supplies,74100,
506,DISPENSER BEVRG,SYRUP BASE ORG CRSH BIB,Soft Beverage Costs,52000, 506,DISPENSER BEVRG,SYRUP BASE ORG CRSH BIB,Soft Beverage Costs,52000,
507,PAPER & DISP,CONTAINER PAPER #4 TAKEOUT WHT,Paper Costs,55000, 507,PAPER & DISP,CONTAINER PAPER #4 TAKEOUT WHT,Paper Costs,55000,
508,CANNED AND DRY,CAPER NONPAREIL IMPORTED,Food Costs,50000, 508,CANNED AND DRY,CAPER NONPAREIL IMPORTED,Food Costs,50000,
@@ -515,7 +515,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
513,PAPER & DISP,FORK PLASTIC WRPD PP XHVY BLK,Paper Costs,55000, 513,PAPER & DISP,FORK PLASTIC WRPD PP XHVY BLK,Paper Costs,55000,
514,MEATS,LAMB RIBLET FZN,Meats Costs,51110, 514,MEATS,LAMB RIBLET FZN,Meats Costs,51110,
515,CANNED AND DRY,WATER BOTTLED,Food Costs,50000, 515,CANNED AND DRY,WATER BOTTLED,Food Costs,50000,
516,CHEMICAL/JANTRL,SOAP HAND LIQ PINK RTU,Food Costs,50000, 516,CHEMICAL/JANTRL,SOAP HAND LIQ PINK RTU,Cleaning Supplies,74100,
517,PAPER & DISP,LINER TRASH 40X46 1.5 ML BLU,Paper Costs,55000, 517,PAPER & DISP,LINER TRASH 40X46 1.5 ML BLU,Paper Costs,55000,
518,CANNED AND DRY,SYSCO CUSTOMER AGREEMENT,Food Costs,50000, 518,CANNED AND DRY,SYSCO CUSTOMER AGREEMENT,Food Costs,50000,
519,CANNED AND DRY,HONEY WILDFLOWER BLOSSOM,Food Costs,50000, 519,CANNED AND DRY,HONEY WILDFLOWER BLOSSOM,Food Costs,50000,
@@ -532,12 +532,12 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
530,CANNED AND DRY,JUICE APPLE GLASS FCY,Food Costs,50000, 530,CANNED AND DRY,JUICE APPLE GLASS FCY,Food Costs,50000,
531,DAIRY PRODUCTS,MILK ALMOND BARISTA BLEND,Dairy Costs,51300, 531,DAIRY PRODUCTS,MILK ALMOND BARISTA BLEND,Dairy Costs,51300,
532,CANNED AND DRY,SPICE OREGANO LEAF,Food Costs,50000, 532,CANNED AND DRY,SPICE OREGANO LEAF,Food Costs,50000,
533,CANNED AND DRY,SAUCE CHILI SRIRACHA CHA,Food Costs,50000, 533,CANNED AND DRY,SAUCE CHILI SRIRACHA CHA,Dry Goods Costs,51500,
534,CANNED AND DRY,OLIVE KALAMATA PTD PLAS KEG,Food Costs,50000, 534,CANNED AND DRY,OLIVE KALAMATA PTD PLAS KEG,Food Costs,50000,
535,CANNED AND DRY,MUSTARD YELLOW PRPD,Food Costs,50000, 535,CANNED AND DRY,MUSTARD YELLOW PRPD,Food Costs,50000,
536,CHEMICAL/JANTRL,CLEANER DEGRSR GREASELIFT RTU,Food Costs,50000, 536,CHEMICAL/JANTRL,CLEANER DEGRSR GREASELIFT RTU,Food Costs,50000,
537,CANNED AND DRY,SALT PKT .6 GM,Food Costs,50000, 537,CANNED AND DRY,SALT PKT .6 GM,Food Costs,50000,
538,CANNED AND DRY,SPICE PEPPER PACKET .1 GM,Food Costs,50000, 538,CANNED AND DRY,SPICE PEPPER PACKET .1 GM,Dry Goods Costs,51500,
539,PAPER & DISP,LINER ROLL COMPOST47X60 1ML,Paper Costs,55000, 539,PAPER & DISP,LINER ROLL COMPOST47X60 1ML,Paper Costs,55000,
540,CANNED AND DRY,WATER SPRKLG ORG ARANCAT CAN,Food Costs,50000, 540,CANNED AND DRY,WATER SPRKLG ORG ARANCAT CAN,Food Costs,50000,
541,DISPENSER BEVRG,SYRUP COKE DIET 5X1 BIB,Soft Beverage Costs,52000, 541,DISPENSER BEVRG,SYRUP COKE DIET 5X1 BIB,Soft Beverage Costs,52000,
@@ -564,7 +564,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
562,CHEMICAL/JANTRL,BLEACH LIQUID DISINFECT CLENER,Food Costs,50000, 562,CHEMICAL/JANTRL,BLEACH LIQUID DISINFECT CLENER,Food Costs,50000,
563,PAPER & DISP,CONTAINER PAPER FBR 9X6 1CPFF,Paper Costs,55000, 563,PAPER & DISP,CONTAINER PAPER FBR 9X6 1CPFF,Paper Costs,55000,
564,CANNED AND DRY,SODA COKE MEXICO GLASS NON RET,Soft Beverage Costs,52000, 564,CANNED AND DRY,SODA COKE MEXICO GLASS NON RET,Soft Beverage Costs,52000,
565,CANNED AND DRY,SPICE CINNAMON STICK,Food Costs,50000, 565,CANNED AND DRY,SPICE CINNAMON STICK,Dry Goods Costs,51500,
566,CANNED AND DRY,WALNUT HALVES AND PCS,Food Costs,50000, 566,CANNED AND DRY,WALNUT HALVES AND PCS,Food Costs,50000,
567,DISPENSER BEVRG,TEA ICED CONC RASP 5.5+1,Soft Beverage Costs,52000, 567,DISPENSER BEVRG,TEA ICED CONC RASP 5.5+1,Soft Beverage Costs,52000,
568,MEATS,BEEF GRND BULK 81/19 CHUB FRS,Beef/Pork Costs,51110, 568,MEATS,BEEF GRND BULK 81/19 CHUB FRS,Beef/Pork Costs,51110,
@@ -658,7 +658,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
656,PAPER & DISP,CUP PLAS TRANS 16OZ SOFT,Paper Costs,55000, 656,PAPER & DISP,CUP PLAS TRANS 16OZ SOFT,Paper Costs,55000,
657,PRODUCE,PARSLEY BUNCH FDSVC,Produce Costs,51200, 657,PRODUCE,PARSLEY BUNCH FDSVC,Produce Costs,51200,
658,PAPER & DISP,LINER TRASH 24X32 .5 ML BLK,Paper Costs,55000, 658,PAPER & DISP,LINER TRASH 24X32 .5 ML BLK,Paper Costs,55000,
659,CANNED AND DRY,SPICE CINNAMON GRND,Food Costs,50000, 659,CANNED AND DRY,SPICE CINNAMON GRND,Dry Goods Costs,51500,
660,PAPER & DISP,FORK PLAS HVY STY BLK,Paper Costs,55000, 660,PAPER & DISP,FORK PLAS HVY STY BLK,Paper Costs,55000,
661,PAPER & DISP,SPOON PLAS PP HVY BLK FULL LEN,Paper Costs,55000, 661,PAPER & DISP,SPOON PLAS PP HVY BLK FULL LEN,Paper Costs,55000,
662,DAIRY PRODUCTS,EGG SHELL LG PAST CF,Dairy Costs,51300, 662,DAIRY PRODUCTS,EGG SHELL LG PAST CF,Dairy Costs,51300,
@@ -797,7 +797,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
795,PAPER & DISP,SUPPLY ACCESSORIES SOTF COM,Paper Costs,55000, 795,PAPER & DISP,SUPPLY ACCESSORIES SOTF COM,Paper Costs,55000,
796,PAPER & DISP,LID PLAS CLR FLT W/SLT 12-24OZ,Paper Costs,55000, 796,PAPER & DISP,LID PLAS CLR FLT W/SLT 12-24OZ,Paper Costs,55000,
797,PAPER & DISP,TRAY PAPER PULP CARRYOUT 4 CUP,Paper Costs,55000, 797,PAPER & DISP,TRAY PAPER PULP CARRYOUT 4 CUP,Paper Costs,55000,
798,CHEMICAL/JANTRL,SANITIZER OASIS 146 MULTI QUAT,Food Costs,50000, 798,CHEMICAL/JANTRL,SANITIZER OASIS 146 MULTI QUAT,Cleaning Supplies,74100,
799,PAPER & DISP,CUP PLAS CLR TALL 8OZ RIGID,Paper Costs,55000, 799,PAPER & DISP,CUP PLAS CLR TALL 8OZ RIGID,Paper Costs,55000,
800,PAPER & DISP,BILLING MISC DISP,Paper Costs,55000, 800,PAPER & DISP,BILLING MISC DISP,Paper Costs,55000,
801,PAPER & DISP,LID FOIL F/ HALF STM TBL PAN,Paper Costs,55000, 801,PAPER & DISP,LID FOIL F/ HALF STM TBL PAN,Paper Costs,55000,
@@ -816,7 +816,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
814,CANNED AND DRY,FLOUR SEMOLINA UNBLCH,Food Costs,50000, 814,CANNED AND DRY,FLOUR SEMOLINA UNBLCH,Food Costs,50000,
815,CANNED AND DRY,SAUCE HOT SRIRACHA BLUE AGAVE,Food Costs,50000, 815,CANNED AND DRY,SAUCE HOT SRIRACHA BLUE AGAVE,Food Costs,50000,
816,PAPER & DISP,LINER TRASH 43X48 16 MC NAT,Paper Costs,55000, 816,PAPER & DISP,LINER TRASH 43X48 16 MC NAT,Paper Costs,55000,
817,CANNED AND DRY,WALNUT HALF & PCS,Food Costs,50000, 817,CANNED AND DRY,WALNUT HALF & PCS,Produce Costs,51200,
818,MEATS,BEEF SHORT RIB ASIAN CUT 1/4,Beef/Pork Costs,51110, 818,MEATS,BEEF SHORT RIB ASIAN CUT 1/4,Beef/Pork Costs,51110,
819,PAPER & DISP,LINER PLAS INSERT/WARMER 18X14,Paper Costs,55000, 819,PAPER & DISP,LINER PLAS INSERT/WARMER 18X14,Paper Costs,55000,
820,PAPER & DISP,BOX PIZZA 14 W/K B-FLT 1-7/8,Paper Costs,55000, 820,PAPER & DISP,BOX PIZZA 14 W/K B-FLT 1-7/8,Paper Costs,55000,
@@ -877,7 +877,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
875,DAIRY PRODUCTS,CHEESE CREAM LIGHT CUP,Dairy Costs,51300, 875,DAIRY PRODUCTS,CHEESE CREAM LIGHT CUP,Dairy Costs,51300,
876,DAIRY PRODUCTS,YOGURT BLUEBERRY GREEK NON FAT,Dairy Costs,51300, 876,DAIRY PRODUCTS,YOGURT BLUEBERRY GREEK NON FAT,Dairy Costs,51300,
877,HLTHCAR/HOSPITALITY,PERKS MEMBERSHIP FEE,Food Costs,50000, 877,HLTHCAR/HOSPITALITY,PERKS MEMBERSHIP FEE,Food Costs,50000,
878,CANNED AND DRY,DRESSING RED WINE VINGRT METRO,Wine Costs,54400, 878,CANNED AND DRY,DRESSING RED WINE VINGRT METRO,Dressing & Sauce Cost,51450,
879,CANNED AND DRY,SPICE GARLIC PWDR,Food Costs,50000, 879,CANNED AND DRY,SPICE GARLIC PWDR,Food Costs,50000,
880,CANNED AND DRY,SPICE ONION POWDER,Food Costs,50000, 880,CANNED AND DRY,SPICE ONION POWDER,Food Costs,50000,
881,CANNED AND DRY,SALT KOSHER FLAKE COARSE,Food Costs,50000, 881,CANNED AND DRY,SALT KOSHER FLAKE COARSE,Food Costs,50000,
@@ -925,7 +925,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
923,CANNED AND DRY,SAUCE WORCESTERSHIRE,Food Costs,50000, 923,CANNED AND DRY,SAUCE WORCESTERSHIRE,Food Costs,50000,
924,PAPER & DISP,CONTAINER PLAS 1C HNG 6X6 WHT,Paper Costs,55000, 924,PAPER & DISP,CONTAINER PLAS 1C HNG 6X6 WHT,Paper Costs,55000,
925,PRODUCE,PINEAPPLE FRESH,Produce Costs,51200, 925,PRODUCE,PINEAPPLE FRESH,Produce Costs,51200,
926,SUPP & EQUIP,MOP HEAD CTN CUT END VALUE #24,Food Costs,50000, 926,SUPP & EQUIP,MOP HEAD CTN CUT END VALUE #24,Cleaning Supplies,74100,
927,DAIRY PRODUCTS,CHEESE RICOTTA WMHM SEL,Dairy Costs,51300, 927,DAIRY PRODUCTS,CHEESE RICOTTA WMHM SEL,Dairy Costs,51300,
928,MEATS,BACON LAYFLAT NT CC 13/17 PR12,Beef/Pork Costs,51110, 928,MEATS,BACON LAYFLAT NT CC 13/17 PR12,Beef/Pork Costs,51110,
929,CANNED AND DRY,COOKIE CRUMB OREO MED CRUNCH,Food Costs,50000, 929,CANNED AND DRY,COOKIE CRUMB OREO MED CRUNCH,Food Costs,50000,
@@ -1005,7 +1005,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1004,DAIRY PRODUCTS,CHEESE BLUE STUFFED OLIVES,Dairy Costs,51300, 1004,DAIRY PRODUCTS,CHEESE BLUE STUFFED OLIVES,Dairy Costs,51300,
1005,SUPP & EQUIP,HANDLE MOP FIBRGLS QUICK CHNGE,Food Costs,50000, 1005,SUPP & EQUIP,HANDLE MOP FIBRGLS QUICK CHNGE,Food Costs,50000,
1006,SUPP & EQUIP,SUPPLY SOTF JANSAN,Food Costs,50000, 1006,SUPP & EQUIP,SUPPLY SOTF JANSAN,Food Costs,50000,
1007,CANNED AND DRY,WALNUT HALVES & PCS,Food Costs,50000, 1007,CANNED AND DRY,WALNUT HALVES & PCS,Produce Costs,51200,
1008,CANNED AND DRY,WATER SPARKLN ORG PRCKLY PEAR,Beverages Costs,52000, 1008,CANNED AND DRY,WATER SPARKLN ORG PRCKLY PEAR,Beverages Costs,52000,
1009,CANNED AND DRY,DRINK NATURAL CLMTN SPRKLG,Food Costs,50000, 1009,CANNED AND DRY,DRINK NATURAL CLMTN SPRKLG,Food Costs,50000,
1010,CANNED AND DRY,DRESSING MIX RNCH BTRMK NO MSG,Food Costs,50000, 1010,CANNED AND DRY,DRESSING MIX RNCH BTRMK NO MSG,Food Costs,50000,
@@ -1032,7 +1032,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1031,CANNED AND DRY,DRESSING BALSAMIC VINGT GARLIC,Food Costs,50000, 1031,CANNED AND DRY,DRESSING BALSAMIC VINGT GARLIC,Food Costs,50000,
1032,CANNED AND DRY,SPREAD CHOC NUTELLA JAR FDSRV,Food Costs,50000, 1032,CANNED AND DRY,SPREAD CHOC NUTELLA JAR FDSRV,Food Costs,50000,
1033,CANNED AND DRY,SUGAR BROWN LIGHT,Food Costs,50000, 1033,CANNED AND DRY,SUGAR BROWN LIGHT,Food Costs,50000,
1034,PAPER & DISP,PAD SCOUR 6X9 HVYDTY ANTIMICRO,Paper Costs,55000, 1034,PAPER & DISP,PAD SCOUR 6X9 HVYDTY ANTIMICRO,Cleaning Supplies,74100,
1035,CHEMICAL/JANTRL,DETERGENT POT/PAN LIQ GRN RTU,Food Costs,50000, 1035,CHEMICAL/JANTRL,DETERGENT POT/PAN LIQ GRN RTU,Food Costs,50000,
1036,PAPER & DISP,KNIFE PLAS HVY STY BLK,Paper Costs,55000, 1036,PAPER & DISP,KNIFE PLAS HVY STY BLK,Paper Costs,55000,
1037,CHEMICAL/JANTRL,CLEANER DISINFECT PEROX RTU,Food Costs,50000, 1037,CHEMICAL/JANTRL,CLEANER DISINFECT PEROX RTU,Food Costs,50000,
@@ -1111,7 +1111,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1110,PAPER & DISP,BAG PAPER BRN W/HNDL REGAL 65#,Paper Costs,55000, 1110,PAPER & DISP,BAG PAPER BRN W/HNDL REGAL 65#,Paper Costs,55000,
1111,DAIRY PRODUCTS,EGG SHELL WHT CAGEFREE GR A LG,Dairy Costs,51300, 1111,DAIRY PRODUCTS,EGG SHELL WHT CAGEFREE GR A LG,Dairy Costs,51300,
1112,CANNED AND DRY,VINEGAR RICE SEASONED,Food Costs,50000, 1112,CANNED AND DRY,VINEGAR RICE SEASONED,Food Costs,50000,
1113,CANNED AND DRY,VINEGAR WHITE DSTD 5%,Food Costs,50000, 1113,CANNED AND DRY,VINEGAR WHITE DSTD 5%,Dry Goods Costs,51500,
1114,SEAFOOD,SHRIMP WHT GH 13-15,Seafood Costs,51130, 1114,SEAFOOD,SHRIMP WHT GH 13-15,Seafood Costs,51130,
1115,PAPER & DISP,BAG PLAS PRTN 6.5X7 ORG SAT,Paper Costs,55000, 1115,PAPER & DISP,BAG PLAS PRTN 6.5X7 ORG SAT,Paper Costs,55000,
1116,CANNED AND DRY,BREAD CRUMB JAP PANKO TOASTED,Food Costs,50000, 1116,CANNED AND DRY,BREAD CRUMB JAP PANKO TOASTED,Food Costs,50000,
@@ -1489,7 +1489,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1488,POULTRY,CHICKEN CVP WHL WOG FZ,Chicken/ Poultry Costs,51120, 1488,POULTRY,CHICKEN CVP WHL WOG FZ,Chicken/ Poultry Costs,51120,
1489,PRODUCE,LEEK BUNCH FRSH ICELS,Produce Costs,51200, 1489,PRODUCE,LEEK BUNCH FRSH ICELS,Produce Costs,51200,
1490,CANNED AND DRY,BILLING MISC CANNED/DRY,Food Costs,50000, 1490,CANNED AND DRY,BILLING MISC CANNED/DRY,Food Costs,50000,
1491,PAPER & DISP,PAN FOIL STEAM TBL HALF DEEP,Paper Costs,55000, 1491,PAPER & DISP,PAN FOIL STEAM TBL HALF DEEP,Dry Goods Costs,51500,
1492,PAPER & DISP,TRAY PAPER CARRIER 4 CUP,Paper Costs,55000, 1492,PAPER & DISP,TRAY PAPER CARRIER 4 CUP,Paper Costs,55000,
1493,PAPER & DISP,KIT CUTLERY FKS/SP/NP HW PP BK,Paper Costs,55000, 1493,PAPER & DISP,KIT CUTLERY FKS/SP/NP HW PP BK,Paper Costs,55000,
1494,MEATS,BEEF PATTY 80/20 RND FRSH,Beef/Pork Costs,51110, 1494,MEATS,BEEF PATTY 80/20 RND FRSH,Beef/Pork Costs,51110,
@@ -1498,7 +1498,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1497,DAIRY PRODUCTS,EGG HARDCOOKED CGFREE HARD PK,Dairy Costs,51300, 1497,DAIRY PRODUCTS,EGG HARDCOOKED CGFREE HARD PK,Dairy Costs,51300,
1498,SEAFOOD,SHRIMP WHT P&D TLOF 26/30,Seafood Costs,51130, 1498,SEAFOOD,SHRIMP WHT P&D TLOF 26/30,Seafood Costs,51130,
1499,PAPER & DISP,CONTAINER PAPER HNG 9X6 1C FBR,Paper Costs,55000, 1499,PAPER & DISP,CONTAINER PAPER HNG 9X6 1C FBR,Paper Costs,55000,
1500,CHEMICAL/JANTRL,DETERGENT POT & PAN LIQUID,Food Costs,50000, 1500,CHEMICAL/JANTRL,DETERGENT POT & PAN LIQUID,Cleaning Supplies,74100,
1501,FROZEN,ASPARAGUS SPEAR MED IQF P,Produce Costs,51200, 1501,FROZEN,ASPARAGUS SPEAR MED IQF P,Produce Costs,51200,
1502,PRODUCE,ASPARAGUS FRESH STANDARD,Produce Costs,51200, 1502,PRODUCE,ASPARAGUS FRESH STANDARD,Produce Costs,51200,
1503,SUPP & EQUIP,SCREEN GRIDDLE 4X6IN,Food Costs,50000, 1503,SUPP & EQUIP,SCREEN GRIDDLE 4X6IN,Food Costs,50000,
@@ -1515,7 +1515,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1514,PAPER & DISP,GLOVE SYNTHETIC FDSRV PF MED,Paper Costs,55000, 1514,PAPER & DISP,GLOVE SYNTHETIC FDSRV PF MED,Paper Costs,55000,
1515,POULTRY,CHICKEN THIGH BNLS SKIN-ON RAW,Chicken/ Poultry Costs,51120, 1515,POULTRY,CHICKEN THIGH BNLS SKIN-ON RAW,Chicken/ Poultry Costs,51120,
1516,CANNED AND DRY,SPICE SAGE GRND,Food Costs,50000, 1516,CANNED AND DRY,SPICE SAGE GRND,Food Costs,50000,
1517,CANNED AND DRY,KETCHUP SQUEEZE RED UPSIDE DWN,Food Costs,50000, 1517,CANNED AND DRY,KETCHUP SQUEEZE RED UPSIDE DWN,Dry Goods Costs,51500,
1518,CANNED AND DRY,SPICE NUTMEG WHL,Food Costs,50000, 1518,CANNED AND DRY,SPICE NUTMEG WHL,Food Costs,50000,
1519,DAIRY PRODUCTS,YOGURT PLAIN FULL FAT,Dairy Costs,51300, 1519,DAIRY PRODUCTS,YOGURT PLAIN FULL FAT,Dairy Costs,51300,
1520,CANNED AND DRY,VINEGAR WINE RED ITALY 6% GLS,Wine Costs,54400, 1520,CANNED AND DRY,VINEGAR WINE RED ITALY 6% GLS,Wine Costs,54400,
@@ -1555,7 +1555,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1554,FROZEN,BAKLAVA WALNT TRIANGLES,Food Costs,50000, 1554,FROZEN,BAKLAVA WALNT TRIANGLES,Food Costs,50000,
1555,PAPER & DISP,DISPENSER NAP XPRSNP STND BLK,Paper Costs,55000, 1555,PAPER & DISP,DISPENSER NAP XPRSNP STND BLK,Paper Costs,55000,
1556,PAPER & DISP,DISPENSER TOWEL MANUL COMP360,Paper Costs,55000, 1556,PAPER & DISP,DISPENSER TOWEL MANUL COMP360,Paper Costs,55000,
1557,PAPER & DISP,PAN FOIL STM TBL MED 2-3/16,Paper Costs,55000, 1557,PAPER & DISP,PAN FOIL STM TBL MED 2-3/16,Dry Goods Costs,51500,
1558,PRODUCE,ONION RED JUMBO CTN,Produce Costs,51200, 1558,PRODUCE,ONION RED JUMBO CTN,Produce Costs,51200,
1559,MEATS,BEEF CHUCK SHORTRIB KOREAN1/2,Beef/Pork Costs,51110, 1559,MEATS,BEEF CHUCK SHORTRIB KOREAN1/2,Beef/Pork Costs,51110,
1560,FROZEN,RICE MEXICAN STY,Food Costs,50000, 1560,FROZEN,RICE MEXICAN STY,Food Costs,50000,
@@ -1653,7 +1653,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1652,CANNED AND DRY,SODA COKE CHERRY ZERO CONTOUR,Soft Beverage Costs,52000, 1652,CANNED AND DRY,SODA COKE CHERRY ZERO CONTOUR,Soft Beverage Costs,52000,
1653,FROZEN,ENTREE VEG FALAFEL BALLS VEGAN,Food Costs,50000, 1653,FROZEN,ENTREE VEG FALAFEL BALLS VEGAN,Food Costs,50000,
1654,SUPP & EQUIP,BRUSH GRILL W/SCRPR 27 IN HNDL,Food Costs,50000, 1654,SUPP & EQUIP,BRUSH GRILL W/SCRPR 27 IN HNDL,Food Costs,50000,
1655,CANNED AND DRY,SAUCE HOT SRIRACHA HUY FONG,Food Costs,50000, 1655,CANNED AND DRY,SAUCE HOT SRIRACHA HUY FONG,Dry Goods Costs,51500,
1656,PAPER & DISP,TOWEL MULTIFOLD PRM LEAF,Paper Costs,55000, 1656,PAPER & DISP,TOWEL MULTIFOLD PRM LEAF,Paper Costs,55000,
1657,CANNED AND DRY,SODA LEMON LIME 12OZ,Soft Beverage Costs,52000, 1657,CANNED AND DRY,SODA LEMON LIME 12OZ,Soft Beverage Costs,52000,
1658,PAPER & DISP,KNIFE PLAS WRP BLK,Paper Costs,55000, 1658,PAPER & DISP,KNIFE PLAS WRP BLK,Paper Costs,55000,
@@ -1709,7 +1709,7 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1708,PAPER & DISP,CONTAINER PLAS CLR HNG 8IN,Paper Costs,55000, 1708,PAPER & DISP,CONTAINER PLAS CLR HNG 8IN,Paper Costs,55000,
1709,PAPER & DISP,TISSUE TOILET 2PL ADVC WHT WR,Paper Costs,55000, 1709,PAPER & DISP,TISSUE TOILET 2PL ADVC WHT WR,Paper Costs,55000,
1710,SUPP & EQUIP,SPATULA RUBBER SILICONE 10.25,Food Costs,50000, 1710,SUPP & EQUIP,SPATULA RUBBER SILICONE 10.25,Food Costs,50000,
1711,CANNED AND DRY,BEAN GARBANZO FCY NO SULFITE,Food Costs,50000, 1711,CANNED AND DRY,BEAN GARBANZO FCY NO SULFITE,Dry Goods Costs,51500,
1712,MEATS,BACON SLI APLWD 13/17CT PR12,Beef/Pork Costs,51110, 1712,MEATS,BACON SLI APLWD 13/17CT PR12,Beef/Pork Costs,51110,
1713,POULTRY,SAUSAGE CHICKEN APPLE RAW 1 OZ,Poultry Costs,51120, 1713,POULTRY,SAUSAGE CHICKEN APPLE RAW 1 OZ,Poultry Costs,51120,
1714,CANNED AND DRY,TOMATO SUNDRIED JULENNE,Food Costs,50000, 1714,CANNED AND DRY,TOMATO SUNDRIED JULENNE,Food Costs,50000,
@@ -1759,38 +1759,73 @@ Id,Sysco Category,Sysco Description,Integreat Account,Integreat Account Code,Nic
1758,MEATS,PORK BELLY SKIN ON P12 COV,Beef/Pork Costs,51110, 1758,MEATS,PORK BELLY SKIN ON P12 COV,Beef/Pork Costs,51110,
1759,MEATS,PORK SHANK BONE KUROBUTA PR12,Beef/Pork Costs,51110, 1759,MEATS,PORK SHANK BONE KUROBUTA PR12,Beef/Pork Costs,51110,
1760,CANNED AND DRY,SEASONING ITALIAN WHL,Food Costs,50000, 1760,CANNED AND DRY,SEASONING ITALIAN WHL,Food Costs,50000,
1761,PRODUCE,MUSHROOM PORTABELLA CAP 4-5,Produce Costs,51200, 1761,PRODUCE,MUSHROOM PORTABELLA CAP 4-5,Produce Costs,51200,
1762,PAPER & DISP,BAG PAPER 250 CT,Paper Costs,55000, 1762,PAPER & DISP,BAG PAPER 250 CT,Paper Costs,55000,
1763,MEATS,BEEF SHLDR TERES MAJOR SEL,Beef/Pork Costs,51110, 1763,MEATS,BEEF SHLDR TERES MAJOR SEL,Beef/Pork Costs,51110,
1764,PAPER & DISP,BOWL PLASTIC COATING 42 OZ,Paper Costs,55000, 1764,PAPER & DISP,BOWL PLASTIC COATING 42 OZ,Paper Costs,55000,
1765,PAPER & DISP,BOX CATERING 21X13X4.25 LOGO,Paper Costs,55000, 1765,PAPER & DISP,BOX CATERING 21X13X4.25 LOGO,Paper Costs,55000,
1766,CANNED AND DRY,CANDY MILK CHOC SHELLS,Food Costs,50000, 1766,CANNED AND DRY,CANDY MILK CHOC SHELLS,Dry Goods Costs,51500,
1767,CANNED AND DRY,CHOCOLATE DUBAI PISTCHO KUNFEH,Food Costs,50000, 1767,CANNED AND DRY,CHOCOLATE DUBAI PISTCHO KUNFEH,Dry Goods Costs,51500,
1768,PAPER & DISP,CONTAINER PAPER 1/30 OZ NTG,Paper Costs,55000, 1768,PAPER & DISP,CONTAINER PAPER 1/30 OZ NTG,Paper Costs,55000,
1769,PAPER & DISP,CONTAINER PAPER 4/110OZ NTG,Paper Costs,55000, 1769,PAPER & DISP,CONTAINER PAPER 4/110OZ NTG,Paper Costs,55000,
1770,PAPER & DISP,CUP PAPER COLD 22 OZ LOGO NTG,Paper Costs,55000, 1770,PAPER & DISP,CUP PAPER COLD 22 OZ LOGO NTG,Paper Costs,55000,
1771,PAPER & DISP,CUP PORTION PLAS CLR 1.50 OZ,Paper Costs,55000, 1771,PAPER & DISP,CUP PORTION PLAS CLR 1.50 OZ,Paper Costs,55000,
1772,CANNED AND DRY,DESSERT CUP,Food Costs,50000, 1772,PAPER & DISP,DESSERT CUP,Paper Costs,55000,
1773,FROZEN,DESSERT MINI PLAIN BEIGNET,Food Costs,50000, 1773,FROZEN,DESSERT MINI PLAIN BEIGNET,Bread and Bun Costs,51400,
1774,CANNED AND DRY,DIP GARLIC TOUM,Food Costs,50000, 1774,CANNED AND DRY,DIP GARLIC TOUM,Dressing & Sauce Cost,51450,
1775,CANNED AND DRY,DRINK ENERGY ORANGE SPRKLNG,Soft Beverage Costs,52000, 1775,CANNED AND DRY,DRINK ENERGY ORANGE SPRKLNG,Soft Beverage Costs,52000,
1776,CANNED AND DRY,DRINK ENERGY PEACH VIBE SPRKLG,Soft Beverage Costs,52000, 1776,CANNED AND DRY,DRINK ENERGY PEACH VIBE SPRKLG,Soft Beverage Costs,52000,
1777,CANNED AND DRY,DRINK ENERGY TROPICAL VIBE,Soft Beverage Costs,52000, 1777,CANNED AND DRY,DRINK ENERGY TROPICAL VIBE,Soft Beverage Costs,52000,
1778,PAPER & DISP,FILM PVC 18X2000 ROLL,Paper Costs,55000, 1778,PAPER & DISP,FILM PVC 18X2000 ROLL,Paper Costs,55000,
1779,CANNED AND DRY,JUICE CONC MANDARIN CARDAMOM,Food Costs,50000, 1779,CANNED AND DRY,JUICE CONC MANDARIN CARDAMOM,Soft Beverage Cost,52000,
1780,CANNED AND DRY,JUICE CONC STRAWB DRAGON,Food Costs,50000, 1780,CANNED AND DRY,JUICE CONC STRAWB DRAGON,Soft Beverage Cost,52000,
1781,PAPER & DISP,LID CLEAR PET 42 OZ,Paper Costs,55000, 1781,PAPER & DISP,LID CLEAR PET 42 OZ,Paper Costs,55000,
1782,PAPER & DISP,LID DOME DESSERT CUP,Paper Costs,55000, 1782,PAPER & DISP,LID DOME DESSERT CUP,Paper Costs,55000,
1783,PAPER & DISP,NAPKIN 2PLY INTR FOLD 6.3X8.26,Paper Costs,55000, 1783,PAPER & DISP,NAPKIN 2PLY INTR FOLD 6.3X8.26,Paper Costs,55000,
1784,CANNED AND DRY,PASTE HERB HARISSA MOROCCAN,Food Costs,50000, 1784,CANNED AND DRY,PASTE HERB HARISSA MOROCCAN,Dressing & Sauce Cost,51450,
1785,CANNED AND DRY,PASTE TAHINI DRESSING,Food Costs,50000, 1785,CANNED AND DRY,PASTE TAHINI DRESSING,Dressing & Sauce Cost,51450,
1786,FROZEN,PASTRY BEIGNET MN FLD CHOCCRML,Food Costs,50000, 1786,FROZEN,PASTRY BEIGNET MN FLD CHOCCRML,Bread and Bun Costs,51400,
1787,CANNED AND DRY,PEPPER BANANA MILD RING,Food Costs,50000, 1787,CANNED AND DRY,PEPPER BANANA MILD RING,Produce Costs,51200,
1788,CANNED AND DRY,RICE MIX NICKS,Food Costs,50000, 1788,CANNED AND DRY,RICE MIX NICKS,Dry Goods Costs,51500,
1789,CANNED AND DRY,SODA CHERRY VISSINADA GREEK,Soft Beverage Costs,52000, 1789,CANNED AND DRY,SODA CHERRY VISSINADA GREEK,Soft Beverage Costs,52000,
1790,CANNED AND DRY,SODA COLA PEPSI ZERO SUGAR,Soft Beverage Costs,52000, 1790,CANNED AND DRY,SODA COLA PEPSI ZERO SUGAR,Soft Beverage Costs,52000,
1791,CANNED AND DRY,SODA PEPSI COLA,Soft Beverage Costs,52000, 1791,CANNED AND DRY,SODA PEPSI COLA,Soft Beverage Costs,52000,
1792,FROZEN,SPANAKOPITA SPINACH COOKED,Food Costs,50000, 1792,FROZEN,SPANAKOPITA SPINACH COOKED,Bread and Bun Costs,51400,
1793,PAPER & DISP,SPOON PLAS TEA PP X-HVY BLK,Paper Costs,55000, 1793,PAPER & DISP,SPOON PLAS TEA PP X-HVY BLK,Paper Costs,55000,
1794,PAPER & DISP,WRAP PAPER 14X14 LOGO VER2,Paper Costs,55000, 1794,PAPER & DISP,WRAP PAPER 14X14 LOGO VER2,Paper Costs,55000,
1795,DAIRY PRODUCTS,YOGURT FRZN NF NICK THE GREEK,Dairy Costs,51300, 1795,DAIRY PRODUCTS,YOGURT FRZN NF NICK THE GREEK,Dairy Costs,51300,
1796,FROZEN,BALL FALAFEL FRTTR 1 OZ IQF,Dry Goods Costs,51500,
1797,SUPP & EQUIP,BASKET PLAS 10.5X7X1.5 BLK,Paperware Cost,55000,
1798,CANNED AND DRY,BEAN GARBANZO LOW SODIUM,Dry Goods Costs,51500,
1799,FROZEN,BREAD POTATO ROLL 4 INCH,Bread and Bun Costs,51400,
1800,FROZEN,BUN HAMBURGER 4IN 1.75 OZ,Bread and Bun Costs,51400,
1801,DAIRY PRODUCTS,CHEESE FETA CRUMBLES,Dairy Costs,51300,
1802,FROZEN,CHEESE STICK HALLOUMI STYL,Dairy Costs,51300,
1803,POULTRY,CHICKEN CVP THGH B/S HALAL,Chicken/ Poultry Costs,51120,
1804,CHEMICAL/JANTRL,CLEANER DEGREASER CONCENTR RTU,Cleaning Supplies,74100,
1805,CHEMICAL/JANTRL,CLEANER DEGREASER GRSELFT RTU,Cleaning Supplies,74100,
1806,,CONTAINER PAPER CUSTOM LOGO8X5,Paperware Cost,55000,
1807,,CONTAINER PAPER FBR 9X6 1C WHT,Paperware Cost,55000,
1808,,DRESSING RANCH SPICY,Dairy Costs,51300,
1809,,FILM PVC ROLL CRYS 2000 FT,Paperware Cost,55000,
1810,,FORK PLASTIC SERVING BLK 10IN,Paperware Cost,55000,
1811,DAIRY PRODUCTS,ICE CREAM COOKIE&CREAM,Dairy Costs,51300,
1812,DAIRY PRODUCTS,ICE CREAM STRAWBERRY,Dairy Costs,51300,
1813,DAIRY PRODUCTS,ICE CREAM VAN QUICK BLEND,Dairy Costs,51300,
1814,DISPENSER BEVRG,JUICE CONC BERRY PATCH ORG,Soft Beverage Cost,52000,
1815,CANNED AND DRY,JUICE LEMON PLAS RTU,Produce Costs,51200,
1816,PRODUCE,KALE CHOPPED,Produce Costs,51200,
1817,PRODUCE,KALE FRESH,Produce Costs,51200,
1818,CANNED AND DRY,KETCHUP FCY POUCH EQUALS 6/10#,Dry Goods Costs,51500,
1819,PRODUCE,LEMON FRESH BAGGED,Produce Costs,51200,
1820,CANNED AND DRY,OIL OLIVE SOYBEAN BLEND 75/25,Dry Goods Costs,51500,
1821,PRODUCE,ONION WHITE JUMBO BAG,Produce Costs,51200,
1822,CANNED AND DRY,PEPPER BANANA MILD RING 7-9HUN,Produce Costs,51200,
1823,CANNED AND DRY,PEPPER BANANA RING,Produce Costs,51200,
1824,FROZEN,POTATO FRY 1/4 SS XLF PHANTM,Produce Costs,51200,
1825,,SANITIZER NO RINSE QUORUM,Cleaning Supplies,74100,
1826,CANNED AND DRY,SODA ROOT BEER CUBE,Soft Beverage Cost,52000,
1827,DISPENSER BEVRG,SYRUP FRUIT PUNCH BIB FLASHIN,Soft Beverage Cost,52000,
1828,DISPENSER BEVRG,SYRUP ORANGE 5X1 BIB,Soft Beverage Cost,52000,
1829,PRODUCE,TOMATO BULK 5X5 FRSH,Produce Costs,51200,
1830,PRODUCE,TOMATO GRAPE FRSH,Produce Costs,51200,
1 Id Sysco Category Sysco Description Integreat Account Integreat Account Code Nick's changes
6 4 PAPER & DISP STRAW PLAS TRANS JMB WRPD 7.75 Paper Costs 55000
7 5 PAPER & DISP FORK PLAS BLK PLA COMPOSTABLE Paper Costs 55000
8 6 PAPER & DISP BAG PLAS LOGO 3 CLR Paper Costs 55000
9 7 FROZEN BREAD PITA GYRO PRE-OILED 7 Food Costs Bread and Bun Costs 50000 51400
10 8 DAIRY PRODUCTS YOGURT FRZN TART Dairy Costs 51300
11 9 POULTRY GYRO CHICKEN SHAWARMA CONE Chicken/ Poultry Costs 51120
12 10 FROZEN BAKLAVA CLASSIC 2X24 Food Costs Dry Goods Costs 50000 51500
13 11 MEATS PORK SLI GYRO CONE Beef/Pork Costs 51110
14 12 DAIRY PRODUCTS SAUCE TZATZIKI Dairy Costs 51300
15 13 POULTRY CHICKEN CVP THIGH BNLS SKLS Chicken/ Poultry Costs 51120
19 17 CANNED AND DRY RICE BASMATI STEAMED XTRA LNG Food Costs 50000
20 18 CANNED AND DRY WATER SPARKLING GREEK Beverages Costs 52000
21 19 DAIRY PRODUCTS SAUCE SPICY YOGURT LOGO Dairy Costs 51300
22 20 CANNED AND DRY WATER PURIFIED .5 Food Costs Soft Beverage Cost 50000 52000
23 21 FROZEN DOUGH PASTRY HNY PUFF Food Costs 50000
24 22 DAIRY PRODUCTS YOGURT PLAIN GREEK NON-FAT Dairy Costs 51300
25 23 PAPER & DISP GLOVE NITRILE LARGE Paper Costs 55000
43 41 CANNED AND DRY WATER BOTTLED DRINKING Food Costs 50000
44 42 CANNED AND DRY SODA CHERRY VISSINADA GRK PLAS Food Costs 50000
45 43 CANNED AND DRY SODA LEMON LEMONADA GREEK Soft Beverage Costs 52000
46 44 CANNED AND DRY WATER MINERAL CARNONATED GREEK Food Costs Soft Beverage Cost 50000 52000
47 45 DISPENSER BEVRG SYRUP COLA PEPSI BIB Soft Beverage Costs 52000
48 46 DISPENSER BEVRG SYRUP LEMONADE PNK BIB Soft Beverage Costs 52000
49 47 CANNED AND DRY RICE BASMATI PABROIL SELA CS Food Costs Dry Goods Costs 50000 51500
50 48 CANNED AND DRY KETCHUP FANCY Food Costs Dry Goods Costs 50000 51500
51 49 CANNED AND DRY TUB & HUMMUS Food Costs 50000
52 50 CHEMICAL/JANTRL SANITIZER MULTI QUAT LIQ Food Costs Cleaning Supplies 50000 74100
53 51 DAIRY PRODUCTS YOGURT PLAIN GRK 5% Dairy Costs 51300
54 52 FROZEN APTZR VEG FALAFEL BALL Food Costs Dry Goods Costs 50000 51500
55 53 PAPER & DISP BOWL PAPER FIBER RND 32OZ 8IN Paper Costs 55000
56 54 PAPER & DISP LID PLAS F/BOWL RND 8 Paper Costs 55000
57 55 MEATS BEEF GRND CHUCK FINE 80/20FRSH Beef/Pork Costs 51110
58 56 CANNED AND DRY SODA ORANGE CRSH Food Costs Soft Beverage Cost 50000 52000
59 57 PAPER & DISP CONTAINER PLAS CLR BAR LK 5 IN Paper Costs 55000
60 58 CANNED AND DRY KETCHUP PACKET FCY Food Costs Dry Goods Costs 50000 51500
61 59 PAPER & DISP BAG PLAS WAVE TOP LOGO 18X16 Paper Costs 55000
62 60 CANNED AND DRY DRESSING VINAIGRETTE LOGO Food Costs Dressing & Sauce Cost 50000 51450
63 61 PAPER & DISP CONTAINER PAPER MLD FBR 9X6 Paper Costs 55000
64 62 PAPER & DISP BOWL PAPER MLD FBR 32OZ NFA Paper Costs 55000
65 63 PAPER & DISP CONTAINER PLAS 120Z SUNDAE Paper Costs 55000
66 64 CANNED AND DRY DRESSING VINAIGRETTE GYRO Food Costs 50000
67 65 CANNED AND DRY VINEGAR WINE RED 5% 50 GRN Alcohol Costs 54000
68 66 DAIRY PRODUCTS EGG SHELL LG WHT AA CA CGFREE Dairy Costs 51300
69 67 CANNED AND DRY DRESSING MARINADE SOUVLAKI Food Costs Dressing & Sauce Cost 50000 51450
70 68 CANNED AND DRY SAUCE MUSTARD Food Costs Dressing & Sauce Cost 50000 51450
71 69 CANNED AND DRY TEA ICED SWEET PURELEAF Beverages Costs 52000
72 70 PAPER & DISP LINER TRASH 40X46 1.1 ML GRY Paper Costs 55000
73 71 CANNED AND DRY SODA COLA Soft Beverage Costs 52000
74 72 CANNED AND DRY HONEY PURE CLOVER GR A TSC JUG Food Costs Dry Goods Costs 50000 51500
75 73 DAIRY PRODUCTS CHEESE FETA RW Dairy Costs 51300
76 74 CANNED AND DRY WATER PURIFIED BTL PET LSE DW Food Costs Soft Beverage Cost 50000 52000
77 75 PRODUCE JUICE LEMON FRESH PSTRZD Produce Costs 51200
78 76 CANNED AND DRY SPREAD HUMMUS TRADITIONAL Food Costs Dressing & Sauce Cost 50000 51450
79 77 PRODUCE LETTUCE ROMAINE OF HEART FRSH Produce Costs 51200
80 78 PAPER & DISP CONTAINER PAPER #1/30OZ NTG Paper Costs 55000
81 79 CANNED AND DRY OIL SALAD CANOLA ZTF Food Costs 50000
84 82 POULTRY CHICKEN CVP WHL WOG NAE 3.5-4# Chicken/ Poultry Costs 51120
85 83 PAPER & DISP GLOVE NITRILE FDSRV PF BLU LRG Paper Costs 55000
86 84 CANNED AND DRY RICE BASMATI CHEF SECRT LG GRN Food Costs 50000
87 85 FROZEN APTZR VEG FALAFEL PUCK HALAL Food Costs Dry Goods Costs 50000 51500
88 86 PAPER & DISP LID PLAS PET FOR 32OZ BOWL Paper Costs 55000
89 87 PAPER & DISP FORK PLAS PP X-HVY BLK Paper Costs 55000
90 88 PRODUCE TOMATO ROMA JUMBO FRESH Produce Costs 51200
95 93 PRODUCE SPINACH BABY FRSH Produce Costs 51200
96 94 PRODUCE DILL BABY FRESH HERB Produce Costs 51200
97 95 PRODUCE CUCUMBER ENGLISH MED SEEDLESS Produce Costs 51200
98 96 CANNED AND DRY SODA ORANGE PORTOKALADA GREEK Food Costs Soft Beverage Cost 50000 52000
99 97 DAIRY PRODUCTS BUTTER SOLID USDA AA UNSLTD Dairy Costs 51300
100 98 DAIRY PRODUCTS CHEESE MONT JACK SLI INT .75OZ Dairy Costs 51300
101 99 DAIRY PRODUCTS CREAMER HALF & HALF SHF STBL Dairy Costs 51300
115 113 CANNED AND DRY FLOUR ALL PURP H&R BL EN MT Food Costs 50000
116 114 CANNED AND DRY JAM STRAWBERRY CUP Food Costs 50000
117 115 CANNED AND DRY MARMALADE ORANGE CUP Food Costs 50000
118 116 CANNED AND DRY SALT GRANULATED PLAIN Food Costs Dry Goods Costs 50000 51500
119 117 CANNED AND DRY SAUCE HOT PEPPER CALIFN STYLE Food Costs 50000
120 118 CANNED AND DRY SAUCE STEAK GLASS Food Costs 50000
121 119 CANNED AND DRY SHORTENING PAN & GRILL Food Costs 50000
122 120 CANNED AND DRY SUGAR GRANULATED XFINE CANE Food Costs Dry Goods Costs 50000 51500
123 121 CANNED AND DRY SYRUP BREAKFAST CUP Food Costs 50000
124 122 CANNED AND DRY SYRUP PANCAKE & WAFFLE Food Costs 50000
125 123 PAPER & DISP BAG PLAS TSHRT 11.5X6.5X21 TKU Paper Costs 55000
126 124 PAPER & DISP CONTAINER PLAS DELI TRANS W/LD Paper Costs 55000
127 125 PAPER & DISP CONTAINER PLAS HNG WHT 8.5 1C Paper Costs 55000
128 126 PAPER & DISP CUP PLAS PRTN TRANS 2OZ Paper Costs 55000
129 127 PAPER & DISP FOIL ALMN ROLL STD WGT 500FT Paper Costs Dry Goods Costs 55000 51500
130 128 PAPER & DISP LINER TRASH 40X46 1.6 ML BLK Paper Costs 55000
131 129 PAPER & DISP TOWEL MULTI 9.5X9.12 EARTH+ Paper Costs 55000
132 130 PRODUCE ASPARAGUS FRESH LARGE FX Produce Costs 51200
155 153 DAIRY PRODUCTS MILK WHL CORRUGATE Dairy Costs 51300
156 154 DAIRY PRODUCTS BUTTERMILK 1% HG Dairy Costs 51300
157 155 DAIRY PRODUCTS CHEESE CHDR MLD SLI INT .75 YL Dairy Costs 51300
158 156 CHEMICAL/JANTRL BLEACH LIQ GRMCDL ULTRA 6% Food Costs Cleaning Supplies 50000 74100
159 157 POULTRY TURKEY BRST NAT BRN PAN SKON Poultry Costs 51120
160 158 DAIRY PRODUCTS CREAM HEAVY 40% FRESH HG Dairy Costs 51300
161 159 MEATS BACON SHINGLE 10/12 HY GF PR12 Beef/Pork Costs 51110
194 192 PAPER & DISP BOWL PAPER MOLDED FIBER 32OZ Paper Costs 55000
195 193 PAPER & DISP BOX CORR CATER #1 LOGO 2021 Paper Costs 55000
196 194 DISPENSER BEVRG SYRUP LEMONADE BIB Soft Beverage Costs 52000
197 195 PAPER & DISP FOIL ALMN ROLL HVY WGT 500 FT Paper Costs Dry Goods Costs 55000 51500
198 196 DAIRY PRODUCTS CHEESE FETA CHUNKS IN BRNE Dairy Costs 51300
199 197 POULTRY CHICKEN CVP WOG WHL HAL Chicken/ Poultry Costs 51120
200 198 PAPER & DISP CONTAINER MFPP 1C HNG 9X6 WHT Paper Costs 55000
203 201 DISPENSER BEVRG SYRUP LEMON LIME BIB Soft Beverage Costs 52000
204 202 DAIRY PRODUCTS CHEESE FETA PAIL Dairy Costs 51300
205 203 CANNED AND DRY CHGS FOR MINIMUM ORDER Food Costs 50000
206 204 CANNED AND DRY BREAD CRUMB PLAIN MED Food Costs Dry Goods Costs 50000 51500
207 205 CANNED AND DRY DRESSING SALAD PRASINI Food Costs Dressing & Sauce Cost 50000 51450
208 206 CANNED AND DRY OIL CORN Food Costs Dressing & Sauce Cost 50000 51450
209 207 CANNED AND DRY OLIVE KALAMATA PTD BRNE 22 LB Food Costs Produce Costs 50000 51200
210 208 CANNED AND DRY SPICE TURMERIC GROUND Food Costs Dry Goods Costs 50000 51500
211 209 PRODUCE SQUASH ZUCCHINI MEDIUM FRESH Produce Costs 51200
212 210 PRODUCE ONION GREEN ICELS ROOTLESS Produce Costs 51200
213 211 CANNED AND DRY SODA COLA PEPSI ZERO Soft Beverage Costs 52000
219 217 FROZEN BAKLAVA GREEK PASTRY Food Costs 50000
220 218 DISPENSER BEVRG SYRUP DR PPR DIET BIB Soft Beverage Costs 52000
221 219 DISPENSER BEVRG SYRUP MOUNTAIN DEW BIB Soft Beverage Costs 52000
222 220 CANNED AND DRY SAUCE CHILI HOT SRIRACHA Food Costs Dry Goods Costs 50000 51500
223 221 PAPER & DISP SKEWER BAMBOO 10IN Paper Costs 55000
224 222 CANNED AND DRY RICE BASMATI Food Costs 50000
225 223 PAPER & DISP WRAP PAPER 14X14 LOGO Paper Costs 55000
227 225 MEATS PORK BUTT BNLS VP PR12 Beef/Pork Costs 51110
228 226 DAIRY PRODUCTS YOGURT PLAIN GREEK NONFAT Dairy Costs 51300
229 227 PAPER & DISP TONG PLAS 9 BLK SNAP N SERVE Paper Costs 55000
230 228 CANNED AND DRY SAUCE HOT SRIRACHA Food Costs Dry Goods Costs 50000 51500
231 229 DISPENSER BEVRG SYRUP DR PEPPER BIB Soft Beverage Costs 52000
232 230 HLTHCAR/HOSPITALITY BILLING MISC REGULAR Food Costs 50000
233 231 PAPER & DISP FORK PLAS BLK MEDHVY MDLNGTH Paper Costs 55000
234 232 DAIRY PRODUCTS YOGURT PLAIN ORIGINAL FTFR Dairy Costs 51300
235 233 SUPP & EQUIP MOP HEAD BLND LPD ALL PURP LRG Food Costs Cleaning Supplies 50000 74100
236 234 PAPER & DISP SPOON PLAS WHT MEDHVY MDLNGTH Paper Costs 55000
237 235 MEATS BEEF GROUND BULK NAT 80/20 Beef/Pork Costs 51110
238 236 PAPER & DISP CONTAINER PAPER HNG 9X6 PFF Paper Costs 55000
245 243 PRODUCE CUCUMBER ENGLISH FRSH Produce Costs 51200
246 244 PAPER & DISP FORK PLAS WHT MED HVY MDLNGTH Paper Costs 55000
247 245 PAPER & DISP CUP PLAS 12-14OZ CLR STRT WALL Paper Costs 55000
248 246 CANNED AND DRY SAUCE HOT BOTTLE Food Costs Dry Goods Costs 50000 51500
249 247 CANNED AND DRY OIL OLIVE BLEND 80/20 Food Costs Dry Goods Costs 50000 51500
250 248 CANNED AND DRY SPICE OREGANO LEAF RUBBED Food Costs Dry Goods Costs 50000 51500
251 249 DAIRY PRODUCTS YOGURT VANILLA GREEK NFAT Dairy Costs 51300
252 250 FROZEN BUN BRIOCHE HOMESTYLE 4 Food Costs 50000
253 251 CANNED AND DRY WATER SPRKLG IMPRTD MNERAL GLS Food Costs 50000
260 258 CANNED AND DRY PEPPER GREEN CHILI WHL Food Costs 50000
261 259 CANNED AND DRY PEPPER JALAPENO SLI FIELD RUN Food Costs 50000
262 260 CANNED AND DRY SAUCE MIX HOLLANDAISE GF Food Costs 50000
263 261 CANNED AND DRY VINEGAR DISTILLED WHITE 5% Food Costs Dry Goods Costs 50000 51500
264 262 PAPER & DISP COVER TOILET SEAT Paper Costs 55000
265 263 PAPER & DISP FILM PVC 2000FT ROLL Paper Costs 55000
266 264 PAPER & DISP DOILY PAPER NRMDY LACE 6 Paper Costs 55000
267 265 PAPER & DISP KIT CUTLERY MED PP KFS S&P NAP Paper Costs 55000
268 266 PAPER & DISP NAPKIN DNR 2P 15X16.25 1/8F WH Paper Costs 55000
269 267 CHEMICAL/JANTRL DETERGENT POT/PAN LIQ PINK RTU Food Costs Cleaning Supplies 50000 74100
270 268 CHEMICAL/JANTRL SALT GRANULE SOLAR WATER SOFT Food Costs 50000
271 269 PRODUCE CARROT FRESH JUMBO Produce Costs 51200
272 270 PRODUCE LIME FRESH 200CT Produce Costs 51200
273 271 PAPER & DISP CUP PLAS RPET CLR 16 OZ Paper Costs 55000
274 272 CANNED AND DRY SAUCE HOT Food Costs Dry Goods Costs 50000 51500
275 273 MEATS BACON SHINGLE 10/12 AW GF PR12 Beef/Pork Costs 51110
276 274 PAPER & DISP CRAYON RED BLUE YEL GREEN Paper Costs 55000
277 275 DAIRY PRODUCTS CREAMER HALF AND HALF PC ASEP Dairy Costs 51300
282 280 CANNED AND DRY KETCHUP SQUEEZE UPSIDE DOWN Food Costs 50000
283 281 PAPER & DISP GLOVE NITRILE FDSRV PF BLK LRG Paper Costs 55000
284 282 MEATS BEEF GRND CHUCK 81/19 CHUB FRS Beef/Pork Costs 51110
285 283 PAPER & DISP LID FOIL F/FULL STM TBL PAN Paper Costs Dry Goods Costs 55000 51500
286 284 PAPER & DISP FORK WOODEN DISP Paper Costs 55000
287 285 PAPER & DISP SKEWER BAMBOO THIN 8 IN Paper Costs 55000
288 286 PAPER & DISP WRAP DELI WHT 12X12 GRS RESIST Paper Costs 55000
292 290 PAPER & DISP TRAY FOOD PAPER #2 LOGO Paper Costs 55000
293 291 PAPER & DISP LID PLAS CLR F/1.5-2.5OZ PRTN Paper Costs 55000
294 292 DISPENSER BEVRG SYRUP TEA RASP 5X1 BRISK Soft Beverage Costs 52000
295 293 PAPER & DISP PAN FOIL STM TBL FULL DP 3-3/8 Paper Costs Dry Goods Costs 55000 51500
296 294 DISPENSER BEVRG SYRUP ROOT BEER BIB Soft Beverage Costs 52000
297 295 PAPER & DISP FOIL ALMN ROLL HVY WGT 1000 FT Paper Costs Dry Goods Costs 55000 51500
298 296 DAIRY PRODUCTS CHEESE GORGONZOLA WHEEL HALF Dairy Costs 51300
299 297 DAIRY PRODUCTS CHEESE MOZZ WM SHRED GOLD PREM Dairy Costs 51300
300 298 DAIRY PRODUCTS CREAM SOUR CULTRD GRADE A Dairy Costs 51300
311 309 SEAFOOD SCALLOP SEA WTR ADD 10/20 USA Seafood Costs 51130
312 310 MEATS BACON SLAB SLI 13/17 CT PR12 Beef/Pork Costs 51110
313 311 CANNED AND DRY DRESSING MIX RANCH Food Costs 50000
314 312 FROZEN BUN BRIOCHE HOMESTYLE 4.25 Food Costs Bread and Bun Costs 50000 51400
315 313 CANNED AND DRY SODA LEMON LIME Soft Beverage Costs 52000
316 314 FROZEN PUREE ORANGE BLOOD CONCENTRATE Food Costs 50000
317 315 CANNED AND DRY FLOUR HI-GLUTEN BL EN MT AA Dry Good Costs 51500
327 325 DAIRY PRODUCTS CHEESE FETA CHUNKS PAIL PREM Dairy Costs 51300
328 326 PRODUCE PEPPER GREEN BELL LARGE FRESH Produce Costs 51200
329 327 PRODUCE TOMATO ROMA FRSH Produce Costs 51200
330 328 CHEMICAL/JANTRL CLEANER DEGREASER OVEN RTU Food Costs Cleaning Supplies 50000 74100
331 329 PAPER & DISP BOX CORR CATER #4 LOGO 2021 Paper Costs 55000
332 330 PAPER & DISP LID PLAS HI DOME DESSRT Paper Costs 55000
333 331 MEATS PORK BUTT BNLS 1/4 6-9#EA Beef/Pork Costs 51110
334 332 PAPER & DISP CUP PAPER HOT WHT TALL 12OZ Paper Costs 55000
335 333 PAPER & DISP SPOON PLAS SOUP BLACK XHEAVY Paper Costs 55000
336 334 PAPER & DISP FOIL ALMN ROLL STD WGT 1000 FT Paper Costs Dry Goods Costs 55000 51500
337 335 SUPP & EQUIP PAD SCRUB STNLS 50GR 1.75OZ Food Costs Cleaning Supplies 50000 74100
338 336 DISPENSER BEVRG SYRUP TEA UNSWTD 5X1 Soft Beverage Costs 52000
339 337 FROZEN BUN BRIOCHE SPLIT TOP 4IN SLI Food Costs Bread and Bun Costs 50000 51400
340 338 CANNED AND DRY WATER MINERAL LIMONATA CAN Beverages Costs 52000
341 339 PRODUCE GARLIC PEELED CHINESE Produce Costs 51200
342 340 PAPER & DISP LID PLAS FLAT F/12-24Z PET CUP Paper Costs 55000
343 341 FROZEN BILLING MISC FROZEN Food Costs 50000
344 342 PAPER & DISP KNIFE PLAS BLK PLA COMPSTABLE Paper Costs 55000
345 343 PAPER & DISP GLOVE NITRILE MED Paper Costs 55000
346 344 SUPP & EQUIP GRILL BRICK 3.5IN THICK Food Costs Cleaning Supplies 50000 74100
347 345 PRODUCE CABBAGE SAVOY FRSH Produce Costs 51200
348 346 PRODUCE FLOWER ORCHID MULTI COLORED Produce Costs 51200
349 347 CANNED AND DRY OIL AVOCADO Food Costs 50000
393 391 PAPER & DISP TOWEL MULTIFOLD 9.4X9.2 WHT 1P Paper Costs 55000
394 392 PAPER & DISP TISSUE TOILET WRPD 4X3.8 2PLY Paper Costs 55000
395 393 MEATS BEEF GRND 80/20 BULK Beef/Pork Costs 51110
396 394 CANNED AND DRY MAYONNAISE HEAVY DUTY Food Costs Dressing & Sauce Cost 50000 51450
397 395 PAPER & DISP CONTAINER PLAS HNG 9X6 WHT Paper Costs 55000
398 396 PAPER & DISP SPOON PLASTIC BLK BAGGED Paper Costs 55000
399 397 PAPER & DISP FORK PLAS BLK HVY FULL LNGTH Paper Costs 55000
406 404 CANNED AND DRY BEAN BLACK Food Costs 50000
407 405 CANNED AND DRY SYRUP CHOCOLATE PLAS JUG Food Costs 50000
408 406 CANNED AND DRY TUNA LIGHT SKIPJACK CHUNK WTR Food Costs 50000
409 407 CHEMICAL/JANTRL DEGREASER HEAVY DUTY RTU Food Costs Cleaning Supplies 50000 74100
410 408 CANNED AND DRY JAM BLACKBERRY CUP Food Costs 50000
411 409 CANNED AND DRY PICKLE WHL DILL KO REF 75/85 Food Costs 50000
412 410 DAIRY PRODUCTS CHEESE QUESO FRESCO CASERO Dairy Costs 51300
423 421 PAPER & DISP CONTAINER PAPER HNG 9X6 FIB Paper Costs 55000
424 422 PAPER & DISP LINER REPRO 40X46 1.5 ML BLK Paper Costs 55000
425 423 PAPER & DISP TAPE PAPR REG THERMAL 3-1/8 Paper Costs 55000
426 424 SUPP & EQUIP PAD SCOUR GRN 6X9IN ANTIMICRO Food Costs Cleaning Supplies 50000 74100
427 425 PAPER & DISP GLOVE NITRILE BLUE XL Paper Costs 55000
428 426 PRODUCE CUCUMBER ENGLISH LONG Produce Costs 51200
429 427 PRODUCE TOMATO ROMA MED Produce Costs 51200
434 432 POULTRY CHICKEN CVP THIGH B/S HALAL JM Chicken/ Poultry Costs 51120
435 433 CANNED AND DRY DRESSING HONEY MUSTARD Food Costs 50000
436 434 PRODUCE JUICE LEMON PSTRZD ULTRA PREM Produce Costs 51200
437 435 CANNED AND DRY SPICE PAPRIKA GROUND Food Costs Dry Goods Costs 50000 51500
438 436 PAPER & DISP FORK PLAS WHT HVY FULL LENGTH Paper Costs 55000
439 437 PAPER & DISP LID FOIL F/ HALF STMTBL PAN Paper Costs Dry Goods Costs 55000 51500
440 438 SUPP & EQUIP PAN FOIL HALF DEEP 100CT Food Costs 50000
441 439 PAPER & DISP CONTAINER FOAM HNG LRG 1C Paper Costs 55000
442 440 PAPER & DISP LINER TRASH 40X48 13 MC NAT Paper Costs 55000
443 441 CANNED AND DRY KETCHUP FCY Food Costs Dry Goods Costs 50000 51500
444 442 PAPER & DISP STRAW PLAS WRPD FLEX WHT 7.625 Paper Costs 55000
445 443 CANNED AND DRY WATER SPRKLG CHRY/POMGRNT Food Costs 50000
446 444 PAPER & DISP APRON POLY EMBSD WHT 28X46 ECO Paper Costs 55000
457 455 SEAFOOD CALAMARI TUBE & TNT 5-8 INCH Seafood Costs 51130
458 456 SEAFOOD SHRIMP CKD & PLD BAY 90\150 FZ Seafood Costs 51130
459 457 SEAFOOD SHRIMP WHT GH 16-20 Seafood Costs 51130
460 458 SUPP & EQUIP BROOM ANGULAR FLAGGED Food Costs Cleaning Supplies 50000 74100
461 459 CANNED AND DRY SAUCE CHILI SRIRACHA Food Costs 50000
462 460 PAPER & DISP CONTAINER PAPER #1 TK OUT KRFT Paper Costs 55000
463 461 PAPER & DISP SPOON PLAS BLK MEDHVY MDLNGTH Paper Costs 55000
464 462 PAPER & DISP LID PLAS F/ 12/16/21/24 CUPS Paper Costs 55000
465 463 PAPER & DISP GLOVE NITRILE FDSRV PF BLU XL Paper Costs 55000
466 464 CANNED AND DRY SODA ORANGE Food Costs Soft Beverage Cost 50000 52000
467 465 PRODUCE SQUASH ZUCCHINI MED FRSH Produce Costs 51200
468 466 CANNED AND DRY SPICE TURMERIC GRND ORGANIC Food Costs 50000
469 467 CANNED AND DRY WATER SPRING IMPORTED GLS Food Costs 50000
474 472 PAPER & DISP GLOVE NITRILE BLK PEDRFREE LRG Paper Costs 55000
475 473 PAPER & DISP TRAY CARRYOUT 4CUP Paper Costs 55000
476 474 PAPER & DISP BAG PLAS T-SHRT THNKYOU12X7X22 Paper Costs 55000
477 475 PAPER & DISP GRILL BRICK 3.5IN THICK Paper Costs Cleaning Supplies 55000 74100
478 476 PAPER & DISP PAD SCOUR GRN 6X9IN ANTIMICRO Paper Costs Cleaning Supplies 55000 74100
479 477 PAPER & DISP PAD SCRUB STNLS 50GR 1.75OZ Paper Costs Cleaning Supplies 55000 74100
480 478 PAPER & DISP LID TOGO PLAS F/12-16-32 OZ Paper Costs 55000
481 479 PRODUCE PEPPER GREEN BELL FRSH LG Produce Costs 51200
482 480 DISPENSER BEVRG SYRUP COLA WILD CHERRY Soft Beverage Costs 52000
486 484 PRODUCE TOMATO ROMA FRESH Produce Costs 51200
487 485 DAIRY PRODUCTS YOGURT PLAIN GREEK WM 4% FAT Dairy Costs 51300
488 486 CHEMICAL/JANTRL CLEANER OVEN GREASESTRIP+ NP Food Costs 50000
489 487 FROZEN BUN BRIOCHE SLI 4.5 Food Costs Bread and Bun Costs 50000 51400
490 488 PRODUCE LEMON FRESH Produce Costs 51200
491 489 SUPP & EQUIP DISH SUPREME GLASS Food Costs 50000
492 490 PAPER & DISP CONTAINER POLYETHELYN Paper Costs 55000
493 491 PAPER & DISP FILTER GREASE CONE 10 IN Paper Costs Cleaning Supplies 55000 74100
494 492 PRODUCE SQUASH ZUCCHINI FCY FRESH Produce Costs 51200
495 493 CHEMICAL/JANTRL CLEANER ALL PURPOSE PINE RTU Food Costs Cleaning Supplies 50000 74100
496 494 CANNED AND DRY KETCHUP PACKET FCY FOIL Food Costs Dry Goods Costs 50000 51500
497 495 PRODUCE MUSHROOM PORTABELLA CP LRG FSH Produce Costs 51200
498 496 PAPER & DISP CONTAINER MINERAL 9X6 HNG 1CPT Paper Costs 55000
499 497 PAPER & DISP WRAP DRY WAX DELI HVY 10X10.75 Paper Costs 55000
500 498 PAPER & DISP LID PLAS 12/16/22 OZ CUP Paper Costs 55000
501 499 CHEMICAL/JANTRL POLISH S-S SATIN SHINE ARSL Food Costs Cleaning Supplies 50000 74100
502 500 CANNED AND DRY SPICE MARJORAM LVS Food Costs Dry Goods Costs 50000 51500
503 501 SUPP & EQUIP BOTTLE PLASTIC SQUEEZE WIDEMTH Food Costs Paperware Cost 50000 55000
504 502 PAPER & DISP PAN FOIL STM TBL DEEPXH 2-9/16 Paper Costs Dry Goods Costs 55000 51500
505 503 PAPER & DISP TONG PLAS BLK 6.25IN SM SRVING Paper Costs 55000
506 504 PAPER & DISP FORK PLAS WHT P/P Paper Costs 55000
507 505 CHEMICAL/JANTRL CLEANER DEGRSR HGH TMP GRL RTU Food Costs Cleaning Supplies 50000 74100
508 506 DISPENSER BEVRG SYRUP BASE ORG CRSH BIB Soft Beverage Costs 52000
509 507 PAPER & DISP CONTAINER PAPER #4 TAKEOUT WHT Paper Costs 55000
510 508 CANNED AND DRY CAPER NONPAREIL IMPORTED Food Costs 50000
515 513 PAPER & DISP FORK PLASTIC WRPD PP XHVY BLK Paper Costs 55000
516 514 MEATS LAMB RIBLET FZN Meats Costs 51110
517 515 CANNED AND DRY WATER BOTTLED Food Costs 50000
518 516 CHEMICAL/JANTRL SOAP HAND LIQ PINK RTU Food Costs Cleaning Supplies 50000 74100
519 517 PAPER & DISP LINER TRASH 40X46 1.5 ML BLU Paper Costs 55000
520 518 CANNED AND DRY SYSCO CUSTOMER AGREEMENT Food Costs 50000
521 519 CANNED AND DRY HONEY WILDFLOWER BLOSSOM Food Costs 50000
532 530 CANNED AND DRY JUICE APPLE GLASS FCY Food Costs 50000
533 531 DAIRY PRODUCTS MILK ALMOND BARISTA BLEND Dairy Costs 51300
534 532 CANNED AND DRY SPICE OREGANO LEAF Food Costs 50000
535 533 CANNED AND DRY SAUCE CHILI SRIRACHA CHA Food Costs Dry Goods Costs 50000 51500
536 534 CANNED AND DRY OLIVE KALAMATA PTD PLAS KEG Food Costs 50000
537 535 CANNED AND DRY MUSTARD YELLOW PRPD Food Costs 50000
538 536 CHEMICAL/JANTRL CLEANER DEGRSR GREASELIFT RTU Food Costs 50000
539 537 CANNED AND DRY SALT PKT .6 GM Food Costs 50000
540 538 CANNED AND DRY SPICE PEPPER PACKET .1 GM Food Costs Dry Goods Costs 50000 51500
541 539 PAPER & DISP LINER ROLL COMPOST47X60 1ML Paper Costs 55000
542 540 CANNED AND DRY WATER SPRKLG ORG ARANCAT CAN Food Costs 50000
543 541 DISPENSER BEVRG SYRUP COKE DIET 5X1 BIB Soft Beverage Costs 52000
564 562 CHEMICAL/JANTRL BLEACH LIQUID DISINFECT CLENER Food Costs 50000
565 563 PAPER & DISP CONTAINER PAPER FBR 9X6 1CPFF Paper Costs 55000
566 564 CANNED AND DRY SODA COKE MEXICO GLASS NON RET Soft Beverage Costs 52000
567 565 CANNED AND DRY SPICE CINNAMON STICK Food Costs Dry Goods Costs 50000 51500
568 566 CANNED AND DRY WALNUT HALVES AND PCS Food Costs 50000
569 567 DISPENSER BEVRG TEA ICED CONC RASP 5.5+1 Soft Beverage Costs 52000
570 568 MEATS BEEF GRND BULK 81/19 CHUB FRS Beef/Pork Costs 51110
658 656 PAPER & DISP CUP PLAS TRANS 16OZ SOFT Paper Costs 55000
659 657 PRODUCE PARSLEY BUNCH FDSVC Produce Costs 51200
660 658 PAPER & DISP LINER TRASH 24X32 .5 ML BLK Paper Costs 55000
661 659 CANNED AND DRY SPICE CINNAMON GRND Food Costs Dry Goods Costs 50000 51500
662 660 PAPER & DISP FORK PLAS HVY STY BLK Paper Costs 55000
663 661 PAPER & DISP SPOON PLAS PP HVY BLK FULL LEN Paper Costs 55000
664 662 DAIRY PRODUCTS EGG SHELL LG PAST CF Dairy Costs 51300
797 795 PAPER & DISP SUPPLY ACCESSORIES SOTF COM Paper Costs 55000
798 796 PAPER & DISP LID PLAS CLR FLT W/SLT 12-24OZ Paper Costs 55000
799 797 PAPER & DISP TRAY PAPER PULP CARRYOUT 4 CUP Paper Costs 55000
800 798 CHEMICAL/JANTRL SANITIZER OASIS 146 MULTI QUAT Food Costs Cleaning Supplies 50000 74100
801 799 PAPER & DISP CUP PLAS CLR TALL 8OZ RIGID Paper Costs 55000
802 800 PAPER & DISP BILLING MISC DISP Paper Costs 55000
803 801 PAPER & DISP LID FOIL F/ HALF STM TBL PAN Paper Costs 55000
816 814 CANNED AND DRY FLOUR SEMOLINA UNBLCH Food Costs 50000
817 815 CANNED AND DRY SAUCE HOT SRIRACHA BLUE AGAVE Food Costs 50000
818 816 PAPER & DISP LINER TRASH 43X48 16 MC NAT Paper Costs 55000
819 817 CANNED AND DRY WALNUT HALF & PCS Food Costs Produce Costs 50000 51200
820 818 MEATS BEEF SHORT RIB ASIAN CUT 1/4 Beef/Pork Costs 51110
821 819 PAPER & DISP LINER PLAS INSERT/WARMER 18X14 Paper Costs 55000
822 820 PAPER & DISP BOX PIZZA 14 W/K B-FLT 1-7/8 Paper Costs 55000
877 875 DAIRY PRODUCTS CHEESE CREAM LIGHT CUP Dairy Costs 51300
878 876 DAIRY PRODUCTS YOGURT BLUEBERRY GREEK NON FAT Dairy Costs 51300
879 877 HLTHCAR/HOSPITALITY PERKS MEMBERSHIP FEE Food Costs 50000
880 878 CANNED AND DRY DRESSING RED WINE VINGRT METRO Wine Costs Dressing & Sauce Cost 54400 51450
881 879 CANNED AND DRY SPICE GARLIC PWDR Food Costs 50000
882 880 CANNED AND DRY SPICE ONION POWDER Food Costs 50000
883 881 CANNED AND DRY SALT KOSHER FLAKE COARSE Food Costs 50000
925 923 CANNED AND DRY SAUCE WORCESTERSHIRE Food Costs 50000
926 924 PAPER & DISP CONTAINER PLAS 1C HNG 6X6 WHT Paper Costs 55000
927 925 PRODUCE PINEAPPLE FRESH Produce Costs 51200
928 926 SUPP & EQUIP MOP HEAD CTN CUT END VALUE #24 Food Costs Cleaning Supplies 50000 74100
929 927 DAIRY PRODUCTS CHEESE RICOTTA WMHM SEL Dairy Costs 51300
930 928 MEATS BACON LAYFLAT NT CC 13/17 PR12 Beef/Pork Costs 51110
931 929 CANNED AND DRY COOKIE CRUMB OREO MED CRUNCH Food Costs 50000
1005 1004 DAIRY PRODUCTS CHEESE BLUE STUFFED OLIVES Dairy Costs 51300
1006 1005 SUPP & EQUIP HANDLE MOP FIBRGLS QUICK CHNGE Food Costs 50000
1007 1006 SUPP & EQUIP SUPPLY SOTF JANSAN Food Costs 50000
1008 1007 CANNED AND DRY WALNUT HALVES & PCS Food Costs Produce Costs 50000 51200
1009 1008 CANNED AND DRY WATER SPARKLN ORG PRCKLY PEAR Beverages Costs 52000
1010 1009 CANNED AND DRY DRINK NATURAL CLMTN SPRKLG Food Costs 50000
1011 1010 CANNED AND DRY DRESSING MIX RNCH BTRMK NO MSG Food Costs 50000
1032 1031 CANNED AND DRY DRESSING BALSAMIC VINGT GARLIC Food Costs 50000
1033 1032 CANNED AND DRY SPREAD CHOC NUTELLA JAR FDSRV Food Costs 50000
1034 1033 CANNED AND DRY SUGAR BROWN LIGHT Food Costs 50000
1035 1034 PAPER & DISP PAD SCOUR 6X9 HVYDTY ANTIMICRO Paper Costs Cleaning Supplies 55000 74100
1036 1035 CHEMICAL/JANTRL DETERGENT POT/PAN LIQ GRN RTU Food Costs 50000
1037 1036 PAPER & DISP KNIFE PLAS HVY STY BLK Paper Costs 55000
1038 1037 CHEMICAL/JANTRL CLEANER DISINFECT PEROX RTU Food Costs 50000
1111 1110 PAPER & DISP BAG PAPER BRN W/HNDL REGAL 65# Paper Costs 55000
1112 1111 DAIRY PRODUCTS EGG SHELL WHT CAGEFREE GR A LG Dairy Costs 51300
1113 1112 CANNED AND DRY VINEGAR RICE SEASONED Food Costs 50000
1114 1113 CANNED AND DRY VINEGAR WHITE DSTD 5% Food Costs Dry Goods Costs 50000 51500
1115 1114 SEAFOOD SHRIMP WHT GH 13-15 Seafood Costs 51130
1116 1115 PAPER & DISP BAG PLAS PRTN 6.5X7 ORG SAT Paper Costs 55000
1117 1116 CANNED AND DRY BREAD CRUMB JAP PANKO TOASTED Food Costs 50000
1489 1488 POULTRY CHICKEN CVP WHL WOG FZ Chicken/ Poultry Costs 51120
1490 1489 PRODUCE LEEK BUNCH FRSH ICELS Produce Costs 51200
1491 1490 CANNED AND DRY BILLING MISC CANNED/DRY Food Costs 50000
1492 1491 PAPER & DISP PAN FOIL STEAM TBL HALF DEEP Paper Costs Dry Goods Costs 55000 51500
1493 1492 PAPER & DISP TRAY PAPER CARRIER 4 CUP Paper Costs 55000
1494 1493 PAPER & DISP KIT CUTLERY FKS/SP/NP HW PP BK Paper Costs 55000
1495 1494 MEATS BEEF PATTY 80/20 RND FRSH Beef/Pork Costs 51110
1498 1497 DAIRY PRODUCTS EGG HARDCOOKED CGFREE HARD PK Dairy Costs 51300
1499 1498 SEAFOOD SHRIMP WHT P&D TLOF 26/30 Seafood Costs 51130
1500 1499 PAPER & DISP CONTAINER PAPER HNG 9X6 1C FBR Paper Costs 55000
1501 1500 CHEMICAL/JANTRL DETERGENT POT & PAN LIQUID Food Costs Cleaning Supplies 50000 74100
1502 1501 FROZEN ASPARAGUS SPEAR MED IQF P Produce Costs 51200
1503 1502 PRODUCE ASPARAGUS FRESH STANDARD Produce Costs 51200
1504 1503 SUPP & EQUIP SCREEN GRIDDLE 4X6IN Food Costs 50000
1515 1514 PAPER & DISP GLOVE SYNTHETIC FDSRV PF MED Paper Costs 55000
1516 1515 POULTRY CHICKEN THIGH BNLS SKIN-ON RAW Chicken/ Poultry Costs 51120
1517 1516 CANNED AND DRY SPICE SAGE GRND Food Costs 50000
1518 1517 CANNED AND DRY KETCHUP SQUEEZE RED UPSIDE DWN Food Costs Dry Goods Costs 50000 51500
1519 1518 CANNED AND DRY SPICE NUTMEG WHL Food Costs 50000
1520 1519 DAIRY PRODUCTS YOGURT PLAIN FULL FAT Dairy Costs 51300
1521 1520 CANNED AND DRY VINEGAR WINE RED ITALY 6% GLS Wine Costs 54400
1555 1554 FROZEN BAKLAVA WALNT TRIANGLES Food Costs 50000
1556 1555 PAPER & DISP DISPENSER NAP XPRSNP STND BLK Paper Costs 55000
1557 1556 PAPER & DISP DISPENSER TOWEL MANUL COMP360 Paper Costs 55000
1558 1557 PAPER & DISP PAN FOIL STM TBL MED 2-3/16 Paper Costs Dry Goods Costs 55000 51500
1559 1558 PRODUCE ONION RED JUMBO CTN Produce Costs 51200
1560 1559 MEATS BEEF CHUCK SHORTRIB KOREAN1/2 Beef/Pork Costs 51110
1561 1560 FROZEN RICE MEXICAN STY Food Costs 50000
1653 1652 CANNED AND DRY SODA COKE CHERRY ZERO CONTOUR Soft Beverage Costs 52000
1654 1653 FROZEN ENTREE VEG FALAFEL BALLS VEGAN Food Costs 50000
1655 1654 SUPP & EQUIP BRUSH GRILL W/SCRPR 27 IN HNDL Food Costs 50000
1656 1655 CANNED AND DRY SAUCE HOT SRIRACHA HUY FONG Food Costs Dry Goods Costs 50000 51500
1657 1656 PAPER & DISP TOWEL MULTIFOLD PRM LEAF Paper Costs 55000
1658 1657 CANNED AND DRY SODA LEMON LIME 12OZ Soft Beverage Costs 52000
1659 1658 PAPER & DISP KNIFE PLAS WRP BLK Paper Costs 55000
1709 1708 PAPER & DISP CONTAINER PLAS CLR HNG 8IN Paper Costs 55000
1710 1709 PAPER & DISP TISSUE TOILET 2PL ADVC WHT WR Paper Costs 55000
1711 1710 SUPP & EQUIP SPATULA RUBBER SILICONE 10.25 Food Costs 50000
1712 1711 CANNED AND DRY BEAN GARBANZO FCY NO SULFITE Food Costs Dry Goods Costs 50000 51500
1713 1712 MEATS BACON SLI APLWD 13/17CT PR12 Beef/Pork Costs 51110
1714 1713 POULTRY SAUSAGE CHICKEN APPLE RAW 1 OZ Poultry Costs 51120
1715 1714 CANNED AND DRY TOMATO SUNDRIED JULENNE Food Costs 50000
1759 1758 MEATS PORK BELLY SKIN ON P12 COV Beef/Pork Costs 51110
1760 1759 MEATS PORK SHANK BONE KUROBUTA PR12 Beef/Pork Costs 51110
1761 1760 CANNED AND DRY SEASONING ITALIAN WHL Food Costs 50000
1762 1761 PRODUCE MUSHROOM PORTABELLA CAP 4-5 Produce Costs 51200
1763 1762 PAPER & DISP BAG PAPER 250 CT Paper Costs 55000
1764 1763 MEATS BEEF SHLDR TERES MAJOR SEL Beef/Pork Costs 51110
1765 1764 PAPER & DISP BOWL PLASTIC COATING 42 OZ Paper Costs 55000
1766 1765 PAPER & DISP BOX CATERING 21X13X4.25 LOGO Paper Costs 55000
1767 1766 CANNED AND DRY CANDY MILK CHOC SHELLS Food Costs Dry Goods Costs 50000 51500
1768 1767 CANNED AND DRY CHOCOLATE DUBAI PISTCHO KUNFEH Food Costs Dry Goods Costs 50000 51500
1769 1768 PAPER & DISP CONTAINER PAPER 1/30 OZ NTG Paper Costs 55000
1770 1769 PAPER & DISP CONTAINER PAPER 4/110OZ NTG Paper Costs 55000
1771 1770 PAPER & DISP CUP PAPER COLD 22 OZ LOGO NTG Paper Costs 55000
1772 1771 PAPER & DISP CUP PORTION PLAS CLR 1.50 OZ Paper Costs 55000
1773 1772 CANNED AND DRY PAPER & DISP DESSERT CUP Food Costs Paper Costs 50000 55000
1774 1773 FROZEN DESSERT MINI PLAIN BEIGNET Food Costs Bread and Bun Costs 50000 51400
1775 1774 CANNED AND DRY DIP GARLIC TOUM Food Costs Dressing & Sauce Cost 50000 51450
1776 1775 CANNED AND DRY DRINK ENERGY ORANGE SPRKLNG Soft Beverage Costs 52000
1777 1776 CANNED AND DRY DRINK ENERGY PEACH VIBE SPRKLG Soft Beverage Costs 52000
1778 1777 CANNED AND DRY DRINK ENERGY TROPICAL VIBE Soft Beverage Costs 52000
1779 1778 PAPER & DISP FILM PVC 18X2000 ROLL Paper Costs 55000
1780 1779 CANNED AND DRY JUICE CONC MANDARIN CARDAMOM Food Costs Soft Beverage Cost 50000 52000
1781 1780 CANNED AND DRY JUICE CONC STRAWB DRAGON Food Costs Soft Beverage Cost 50000 52000
1782 1781 PAPER & DISP LID CLEAR PET 42 OZ Paper Costs 55000
1783 1782 PAPER & DISP LID DOME DESSERT CUP Paper Costs 55000
1784 1783 PAPER & DISP NAPKIN 2PLY INTR FOLD 6.3X8.26 Paper Costs 55000
1785 1784 CANNED AND DRY PASTE HERB HARISSA MOROCCAN Food Costs Dressing & Sauce Cost 50000 51450
1786 1785 CANNED AND DRY PASTE TAHINI DRESSING Food Costs Dressing & Sauce Cost 50000 51450
1787 1786 FROZEN PASTRY BEIGNET MN FLD CHOCCRML Food Costs Bread and Bun Costs 50000 51400
1788 1787 CANNED AND DRY PEPPER BANANA MILD RING Food Costs Produce Costs 50000 51200
1789 1788 CANNED AND DRY RICE MIX NICKS Food Costs Dry Goods Costs 50000 51500
1790 1789 CANNED AND DRY SODA CHERRY VISSINADA GREEK Soft Beverage Costs 52000
1791 1790 CANNED AND DRY SODA COLA PEPSI ZERO SUGAR Soft Beverage Costs 52000
1792 1791 CANNED AND DRY SODA PEPSI COLA Soft Beverage Costs 52000
1793 1792 FROZEN SPANAKOPITA SPINACH COOKED Food Costs Bread and Bun Costs 50000 51400
1794 1793 PAPER & DISP SPOON PLAS TEA PP X-HVY BLK Paper Costs 55000
1795 1794 PAPER & DISP WRAP PAPER 14X14 LOGO VER2 Paper Costs 55000
1796 1795 DAIRY PRODUCTS YOGURT FRZN NF NICK THE GREEK Dairy Costs 51300
1797 1796 FROZEN BALL FALAFEL FRTTR 1 OZ IQF Dry Goods Costs 51500
1798 1797 SUPP & EQUIP BASKET PLAS 10.5X7X1.5 BLK Paperware Cost 55000
1799 1798 CANNED AND DRY BEAN GARBANZO LOW SODIUM Dry Goods Costs 51500
1800 1799 FROZEN BREAD POTATO ROLL 4 INCH Bread and Bun Costs 51400
1801 1800 FROZEN BUN HAMBURGER 4IN 1.75 OZ Bread and Bun Costs 51400
1802 1801 DAIRY PRODUCTS CHEESE FETA CRUMBLES Dairy Costs 51300
1803 1802 FROZEN CHEESE STICK HALLOUMI STYL Dairy Costs 51300
1804 1803 POULTRY CHICKEN CVP THGH B/S HALAL Chicken/ Poultry Costs 51120
1805 1804 CHEMICAL/JANTRL CLEANER DEGREASER CONCENTR RTU Cleaning Supplies 74100
1806 1805 CHEMICAL/JANTRL CLEANER DEGREASER GRSELFT RTU Cleaning Supplies 74100
1807 1806 CONTAINER PAPER CUSTOM LOGO8X5 Paperware Cost 55000
1808 1807 CONTAINER PAPER FBR 9X6 1C WHT Paperware Cost 55000
1809 1808 DRESSING RANCH SPICY Dairy Costs 51300
1810 1809 FILM PVC ROLL CRYS 2000 FT Paperware Cost 55000
1811 1810 FORK PLASTIC SERVING BLK 10IN Paperware Cost 55000
1812 1811 DAIRY PRODUCTS ICE CREAM COOKIE&CREAM Dairy Costs 51300
1813 1812 DAIRY PRODUCTS ICE CREAM STRAWBERRY Dairy Costs 51300
1814 1813 DAIRY PRODUCTS ICE CREAM VAN QUICK BLEND Dairy Costs 51300
1815 1814 DISPENSER BEVRG JUICE CONC BERRY PATCH ORG Soft Beverage Cost 52000
1816 1815 CANNED AND DRY JUICE LEMON PLAS RTU Produce Costs 51200
1817 1816 PRODUCE KALE CHOPPED Produce Costs 51200
1818 1817 PRODUCE KALE FRESH Produce Costs 51200
1819 1818 CANNED AND DRY KETCHUP FCY POUCH EQUALS 6/10# Dry Goods Costs 51500
1820 1819 PRODUCE LEMON FRESH BAGGED Produce Costs 51200
1821 1820 CANNED AND DRY OIL OLIVE SOYBEAN BLEND 75/25 Dry Goods Costs 51500
1822 1821 PRODUCE ONION WHITE JUMBO BAG Produce Costs 51200
1823 1822 CANNED AND DRY PEPPER BANANA MILD RING 7-9HUN Produce Costs 51200
1824 1823 CANNED AND DRY PEPPER BANANA RING Produce Costs 51200
1825 1824 FROZEN POTATO FRY 1/4 SS XLF PHANTM Produce Costs 51200
1826 1825 SANITIZER NO RINSE QUORUM Cleaning Supplies 74100
1827 1826 CANNED AND DRY SODA ROOT BEER CUBE Soft Beverage Cost 52000
1828 1827 DISPENSER BEVRG SYRUP FRUIT PUNCH BIB FLASHIN Soft Beverage Cost 52000
1829 1828 DISPENSER BEVRG SYRUP ORANGE 5X1 BIB Soft Beverage Cost 52000
1830 1829 PRODUCE TOMATO BULK 5X5 FRSH Produce Costs 51200
1831 1830 PRODUCE TOMATO GRAPE FRSH Produce Costs 51200

View File

@@ -0,0 +1,43 @@
,,,,,,,,,,,,,,,,,,,,,,,,,d,,,,,,,,,,,,,,,,,,,,,,,,,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,20.56,CA,20.56,Y,0,0,0,1.2,07,48,02,01,CANNED AND DRY,24,20OZ,AQUAFIN,WATER PURIFIED BTL PET LSE DW,30,33,0.75,24,,000000,,0000,,,,29115,47,,8492330,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,14.84,CA,14.84,Y,0,0,0,0.6,07,16,05,01,CANNED AND DRY,12,11.2OZ,LOUX,SODA CHERRY VISSINADA GRK PLAS,9.5,10.5,0.26,12,,000000,,0000,,,3000P,808959,01,,7189422,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,14.93,CA,14.93,Y,0,0,0,0.6,07,16,05,01,CANNED AND DRY,12,8 OZ,LOUX,SODA LEMON LEMONADA GREEK,9,11.5,0.26,12,,000000,,0000,,,3200,808959,01,,9910355,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,79.83,CA,79.83,Y,0,0,0,0,07,35,03,99,CANNED AND DRY,4,5 LB,OTHRYS,SPICE OREGANO LEAF RUBBED,20,22,2.51,4,,000000,,0000,,,62760,808959,01,,9911236,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,31.07,CA,31.07,Y,0,0,0,0,07,35,99,99,CANNED AND DRY,22,4.68OZ,HI WEST,RICE MIX NICKS,6.43,7,0.16,22,,000000,,0000,,,30-5729,345717,03,,7301949,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,54.84,CA,109.68,Y,0,0,0,0,07,33,01,99,CANNED AND DRY,2,20 LB,ROYAL,RICE BASMATI PABROIL SELA CS,40,40.6,1.21,2,,000000,,0000,,,91000244,26992,43,,7053293,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,75.49,CA,75.49,Y,0,0,0,0,07,34,04,99,CANNED AND DRY,4,1 GAL,NICKGRK,DRESSING VINAIGRETTE LOGO,33,35,0.87,4,,000000,,0000,,,1654,853,01,,7108399,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,29.4,CA,29.4,Y,0,0,0,0,07,36,99,99,CANNED AND DRY,8,15 OZ,HAIG'S,DIP GARLIC TOUM,7.25,8.25,0.34,8,,000000,,0000,,,8PGD16,691816,01,,7360056,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,38.85,CA,38.85,Y,0,0,0,0,07,37,02,02,CANNED AND DRY,1,35 LB,BEOCO,OIL CORN,35,36.55,0.85,1,,000000,,0000,,,,9846,02,,4823761,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,56.72,CA,113.44,Y,0,0,0,0,07,36,99,99,CANNED AND DRY,4,4 LB,GRECDEL,SPREAD HUMMUS TRADITIONAL,16,17,0.62,4,,000000,,0000,,,HU000083,1533,19,,7278619,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,-7.22,CA,-7.32,Y,0,0,0,0,07,86,01,99,CANNED AND DRY,1,EA,NONPROD,ALLOWANCE FOR DROP SIZE,0.01,0.01,0.01,1,,000000,,0000,,,,,01,,9477498,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,4.17,CA,4.17,Y,0,0,0,0,07,86,01,99,CANNED AND DRY,1,EA,NONPROD,CHGS FOR FUEL SURCHARGE,1,1,0,1,,000000,,0000,,,,,01,,6592893,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,95.98,CA,191.96,Y,0,0,0,0,02,04,99,99,DAIRY PRODUCTS,1,5 GAL,NICKGRK,SAUCE TZATZIKI,42,43.5,1.2,1,,000000,,0000,,,SA000084,1533,19,,7213639,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,54.82,CA,109.64,Y,0,0,0,0,02,10,01,99,DAIRY PRODUCTS,4,1 GAL,NICKGRK,YOGURT FRZN NF NICK THE GREEK,39.9,39.9,0.97,4,,000000,,0000,,,13101,379887,05,,7302646,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,172.37,CA,172.37,Y,0,0,0,0,12,08,02,03,DISPENSER BEVRG,12,32 OZ,TRACTOR,JUICE CONC STRAWB DRAGON,24,25.5,0.58,12,,000000,,0000,,,6555,693956,01,,7206974,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,62.48,CA,62.48,Y,0,0,0,0,12,08,02,03,DISPENSER BEVRG,1,2.5GAL,DR PEPR,SYRUP DR PPR DIET BIB,20.93,21.82,0.47,1,,000000,,0000,,,12115,376510,09,,7459969,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,117.3,CA,117.3,Y,0,0,0,0,12,08,02,03,DISPENSER BEVRG,1,5GAL,DR PEPR,SYRUP DR PEPPER BIB,40,54.4,0.83,1,,000000,,0000,,,12109,9562,14,,4273553,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,8,8,0,26.36,CA,210.88,Y,0,0,0,0,06,02,45,99,FROZEN,12,10 CT,KONTOS,BREAD PITA GYRO PRE-OILED 7,21,24,1.65,12,,000000,,0000,,,10005,25370,01,,5223334,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,69.37,CA,138.74,Y,0,0,0,0,06,01,70,99,FROZEN,36,6 OZ,HELLAS,SPANAKOPITA SPINACH COOKED,12.4,13.4,0.62,36,,000000,,0000,,,216312,32248,01,,7455027,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,49.53,CA,99.06,Y,0,0,0,0,06,01,60,99,FROZEN,2,24 CT,HELLAS,BAKLAVA CLASSIC 2X24,9.6,10.6,0.46,2,,000000,,0000,,,100224,32248,01,,7187055,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,57.66,CA,57.66,Y,0,0,0,0,06,01,65,99,FROZEN,140,0.7 OZ,CHICPAT,DESSERT MINI PLAIN BEIGNET,6.17,7.5,0.88,140,,000000,,0000,,,540061,1188,53,,7212299,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,97.94,CA,97.94,Y,0,0,0,0,06,02,01,99,FROZEN,4,10 LB,NICKGRK,APTZR VEG FALAFEL PUCK HALAL,40,42,2.03,4,,000000,,0000,,,FA000090,1533,05,,7274591,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,1,53.5,1,53.5,7.556,LB,404.25,Y,0,0,0,0,03,02,01,13,MEATS,5,10.5#,TWORVRS,BEEF SHLDR TERES MAJOR SEL,53,55,1.99,5,,000000,,0000,,,B83003,527004,03,,0932867,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,87.76,CA,87.76,Y,0,0,0,0,03,04,99,99,MEATS,1,20 LB,GRECDEL,PORK SLI GYRO CONE,20,21,0.77,1,,000000,,0000,,,ME000215,1533,05,,7211838,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,7,7,0,92.53,CA,647.71,Y,0,0,0,0,03,02,04,99,MEATS,1,30 LB,NICKGRK,MEAT GYRO BEEF CONE NTG,30,31,0.97,1,,000000,,0000,,,ME000071,1533,05,,9906087,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,25.41,CA,25.41,Y,0,0,0,0,08,42,64,43,PAPER & DISP,20,50 CT,KARAT,LID PLAS FLAT F/12-22 OZ,5.75,7,1.94,20,,000000,,0000,,,C-KCL90,461672,05,,7661388,00000000000000,,,260402,04671945,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,22.32,CA,22.32,Y,0,0,0,0,08,60,99,99,PAPER & DISP,24,250 CT,ELEMEN,NAPKIN 2PLY INTR FOLD 6.3X8.26,16.1,16.8,1.55,24,,000000,,0000,,,11904,613310,01,,7452585,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,26.15,CA,26.15,Y,0,0,0,0,08,21,64,62,PAPER & DISP,50,50CT,KARAT,CUP PORTION PLAS CLR 1.50 OZ,10,10,1.36,50,,000000,,0000,,,FP-P150-PP,461672,05,,4613026,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,43.99,CA,43.99,Y,0,0,0,0,08,75,03,04,PAPER & DISP,6,50 EA,NATZWAY,BOWL PLASTIC COATING 42 OZ,14.55,17.19,3.56,6,,000000,,0000,,,10205,773772,01,,7408008,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,41.25,CA,41.25,Y,0,0,0,0,08,42,99,99,PAPER & DISP,6,50CT,NATZWAY,LID CLEAR PET 42 OZ,8.59,10.47,2.01,6,,000000,,0000,,,10206,773772,01,,7408215,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,47.6,CA,47.6,Y,0,0,0,0,08,21,56,99,PAPER & DISP,1000,22 OZ,NICKGRK,CUP PAPER COLD 22 OZ LOGO NTG,31.96,34.62,3.78,1000,,000000,,0000,,,810161542703,461672,05,,7354127,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,30.6,CA,30.6,Y,0,0,0,0,08,18,56,99,PAPER & DISP,1,450 CT,NICKGRK,CONTAINER PAPER 1/30 OZ NTG,23,25,3.25,1,,000000,,0000,,,810161542673,461672,05,,7354120,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,27.6,CA,27.6,Y,0,0,0,0,08,18,56,99,PAPER & DISP,1,160 CT,NICKGRK,CONTAINER PAPER 4/110OZ NTG,19.6,21.4,3.59,1,,000000,,0000,,,810161542680,461672,05,,7354119,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,3,3,0,37.6,CA,112.8,Y,0,0,0,0,08,18,02,12,PAPER & DISP,2,100CT,NATZWAY,CONTAINER PAPER MLD FBR 9X6,18,18,1.28,2,,000000,,0000,,,10042,773772,01,,7250678,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,2,2,0,46.07,CA,92.14,Y,0,0,0,0,08,09,56,99,PAPER & DISP,1,250BAG,NICKGRK,BAG PAPER 250 CT,14.5,15,2.18,1,,000000,,0000,,,,773772,01,,7417242,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,1,,1,1,0,24.46,EA,24.46,Y,0,0,0,0,08,36,56,79,PAPER & DISP,5,1000,BAGCRFT,WRAP DELI WHT 12X12 GRS RESIST,37,37,1.07,5,,000000,,0000,,,P057012,276,01,,5723808,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,1,1,0,39.41,CA,39.41,Y,3.85,0,0,0,08,06,25,10,PAPER & DISP,10,100CT,DHGPROF,GLOVE NITRILE BLK PEDRFREE LRG,12.21,12.21,0.66,10,,000000,,0000,,,DNGB-L,613310,01,,7296407,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,3,3,0,89.32,CA,267.96,Y,0,0,0,0,05,01,01,07,POULTRY,4,10 LB,SYS CLS,CHICKEN CVP THIGH BNLS SKLS,40,42,1.04,4,,000000,,0000,,,14301,3254,21,,7792187,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,9,9,0,86.97,CA,782.73,Y,0,0,0,0,05,02,01,99,POULTRY,1,20LB,GRECDEL,GYRO CHICKEN SHAWARMA CONE,20,21,0.77,1,,000000,,0000,,,ME000102,1533,05,,7124188,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,DET,,,,,5,5,0,23.65,CA,118.25,Y,0,0,0,0,11,02,23,01,PRODUCE,1,50 LB,PACKER,POTATO KENNEBEC FRESH,50,52,2,1,,000000,,0000,,,,696760,01,,2039220,00000000000000,,,260402,04672959,
EEK,,050,00175469,850081745,HDR,,,CKC CONCORD INC,,260402,,,,Rolling 8,,NICK THE GREEK CONCORD,2075 DIAMOND BLVD,STE H-103,CONCORD,CA,94520-582,408593,000000000,,,,,BBNKG,0,050,SYSCO SAN FRANCISCO,5900 STEWART AVENU,,FREMONT,CA,94538,,,,,,,1372486,4024,004,0000000,00000000,20260529,6.25,CRO8
EEK,,050,00175469,850081745,SUM,,,40,0,0,74,0,4625.36,6.25,00000463161,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1 d
2 EEK 050 00175469 850081745 DET 1 1 0 20.56 CA 20.56 Y 0 0 0 1.2 07 48 02 01 CANNED AND DRY 24 20OZ AQUAFIN WATER PURIFIED BTL PET LSE DW 30 33 0.75 24 000000 0000 29115 47 8492330 00000000000000 260402 04672959
3 EEK 050 00175469 850081745 DET 1 1 0 14.84 CA 14.84 Y 0 0 0 0.6 07 16 05 01 CANNED AND DRY 12 11.2OZ LOUX SODA CHERRY VISSINADA GRK PLAS 9.5 10.5 0.26 12 000000 0000 3000P 808959 01 7189422 00000000000000 260402 04672959
4 EEK 050 00175469 850081745 DET 1 1 0 14.93 CA 14.93 Y 0 0 0 0.6 07 16 05 01 CANNED AND DRY 12 8 OZ LOUX SODA LEMON LEMONADA GREEK 9 11.5 0.26 12 000000 0000 3200 808959 01 9910355 00000000000000 260402 04672959
5 EEK 050 00175469 850081745 DET 1 1 0 79.83 CA 79.83 Y 0 0 0 0 07 35 03 99 CANNED AND DRY 4 5 LB OTHRYS SPICE OREGANO LEAF RUBBED 20 22 2.51 4 000000 0000 62760 808959 01 9911236 00000000000000 260402 04672959
6 EEK 050 00175469 850081745 DET 1 1 0 31.07 CA 31.07 Y 0 0 0 0 07 35 99 99 CANNED AND DRY 22 4.68OZ HI WEST RICE MIX NICKS 6.43 7 0.16 22 000000 0000 30-5729 345717 03 7301949 00000000000000 260402 04672959
7 EEK 050 00175469 850081745 DET 2 2 0 54.84 CA 109.68 Y 0 0 0 0 07 33 01 99 CANNED AND DRY 2 20 LB ROYAL RICE BASMATI PABROIL SELA CS 40 40.6 1.21 2 000000 0000 91000244 26992 43 7053293 00000000000000 260402 04672959
8 EEK 050 00175469 850081745 DET 1 1 0 75.49 CA 75.49 Y 0 0 0 0 07 34 04 99 CANNED AND DRY 4 1 GAL NICKGRK DRESSING VINAIGRETTE LOGO 33 35 0.87 4 000000 0000 1654 853 01 7108399 00000000000000 260402 04672959
9 EEK 050 00175469 850081745 DET 1 1 0 29.4 CA 29.4 Y 0 0 0 0 07 36 99 99 CANNED AND DRY 8 15 OZ HAIG'S DIP GARLIC TOUM 7.25 8.25 0.34 8 000000 0000 8PGD16 691816 01 7360056 00000000000000 260402 04672959
10 EEK 050 00175469 850081745 DET 1 1 0 38.85 CA 38.85 Y 0 0 0 0 07 37 02 02 CANNED AND DRY 1 35 LB BEOCO OIL CORN 35 36.55 0.85 1 000000 0000 9846 02 4823761 00000000000000 260402 04672959
11 EEK 050 00175469 850081745 DET 2 2 0 56.72 CA 113.44 Y 0 0 0 0 07 36 99 99 CANNED AND DRY 4 4 LB GRECDEL SPREAD HUMMUS TRADITIONAL 16 17 0.62 4 000000 0000 HU000083 1533 19 7278619 00000000000000 260402 04672959
12 EEK 050 00175469 850081745 DET 1 1 0 -7.22 CA -7.32 Y 0 0 0 0 07 86 01 99 CANNED AND DRY 1 EA NONPROD ALLOWANCE FOR DROP SIZE 0.01 0.01 0.01 1 000000 0000 01 9477498 00000000000000 260402 04672959
13 EEK 050 00175469 850081745 DET 1 1 0 4.17 CA 4.17 Y 0 0 0 0 07 86 01 99 CANNED AND DRY 1 EA NONPROD CHGS FOR FUEL SURCHARGE 1 1 0 1 000000 0000 01 6592893 00000000000000 260402 04672959
14 EEK 050 00175469 850081745 DET 2 2 0 95.98 CA 191.96 Y 0 0 0 0 02 04 99 99 DAIRY PRODUCTS 1 5 GAL NICKGRK SAUCE TZATZIKI 42 43.5 1.2 1 000000 0000 SA000084 1533 19 7213639 00000000000000 260402 04672959
15 EEK 050 00175469 850081745 DET 2 2 0 54.82 CA 109.64 Y 0 0 0 0 02 10 01 99 DAIRY PRODUCTS 4 1 GAL NICKGRK YOGURT FRZN NF NICK THE GREEK 39.9 39.9 0.97 4 000000 0000 13101 379887 05 7302646 00000000000000 260402 04672959
16 EEK 050 00175469 850081745 DET 1 1 0 172.37 CA 172.37 Y 0 0 0 0 12 08 02 03 DISPENSER BEVRG 12 32 OZ TRACTOR JUICE CONC STRAWB DRAGON 24 25.5 0.58 12 000000 0000 6555 693956 01 7206974 00000000000000 260402 04672959
17 EEK 050 00175469 850081745 DET 1 1 0 62.48 CA 62.48 Y 0 0 0 0 12 08 02 03 DISPENSER BEVRG 1 2.5GAL DR PEPR SYRUP DR PPR DIET BIB 20.93 21.82 0.47 1 000000 0000 12115 376510 09 7459969 00000000000000 260402 04672959
18 EEK 050 00175469 850081745 DET 1 1 0 117.3 CA 117.3 Y 0 0 0 0 12 08 02 03 DISPENSER BEVRG 1 5GAL DR PEPR SYRUP DR PEPPER BIB 40 54.4 0.83 1 000000 0000 12109 9562 14 4273553 00000000000000 260402 04672959
19 EEK 050 00175469 850081745 DET 8 8 0 26.36 CA 210.88 Y 0 0 0 0 06 02 45 99 FROZEN 12 10 CT KONTOS BREAD PITA GYRO PRE-OILED 7 21 24 1.65 12 000000 0000 10005 25370 01 5223334 00000000000000 260402 04672959
20 EEK 050 00175469 850081745 DET 2 2 0 69.37 CA 138.74 Y 0 0 0 0 06 01 70 99 FROZEN 36 6 OZ HELLAS SPANAKOPITA SPINACH COOKED 12.4 13.4 0.62 36 000000 0000 216312 32248 01 7455027 00000000000000 260402 04672959
21 EEK 050 00175469 850081745 DET 2 2 0 49.53 CA 99.06 Y 0 0 0 0 06 01 60 99 FROZEN 2 24 CT HELLAS BAKLAVA CLASSIC 2X24 9.6 10.6 0.46 2 000000 0000 100224 32248 01 7187055 00000000000000 260402 04672959
22 EEK 050 00175469 850081745 DET 1 1 0 57.66 CA 57.66 Y 0 0 0 0 06 01 65 99 FROZEN 140 0.7 OZ CHICPAT DESSERT MINI PLAIN BEIGNET 6.17 7.5 0.88 140 000000 0000 540061 1188 53 7212299 00000000000000 260402 04672959
23 EEK 050 00175469 850081745 DET 1 1 0 97.94 CA 97.94 Y 0 0 0 0 06 02 01 99 FROZEN 4 10 LB NICKGRK APTZR VEG FALAFEL PUCK HALAL 40 42 2.03 4 000000 0000 FA000090 1533 05 7274591 00000000000000 260402 04672959
24 EEK 050 00175469 850081745 DET 1 53.5 1 53.5 7.556 LB 404.25 Y 0 0 0 0 03 02 01 13 MEATS 5 10.5# TWORVRS BEEF SHLDR TERES MAJOR SEL 53 55 1.99 5 000000 0000 B83003 527004 03 0932867 00000000000000 260402 04672959
25 EEK 050 00175469 850081745 DET 1 1 0 87.76 CA 87.76 Y 0 0 0 0 03 04 99 99 MEATS 1 20 LB GRECDEL PORK SLI GYRO CONE 20 21 0.77 1 000000 0000 ME000215 1533 05 7211838 00000000000000 260402 04672959
26 EEK 050 00175469 850081745 DET 7 7 0 92.53 CA 647.71 Y 0 0 0 0 03 02 04 99 MEATS 1 30 LB NICKGRK MEAT GYRO BEEF CONE NTG 30 31 0.97 1 000000 0000 ME000071 1533 05 9906087 00000000000000 260402 04672959
27 EEK 050 00175469 850081745 DET 1 1 0 25.41 CA 25.41 Y 0 0 0 0 08 42 64 43 PAPER & DISP 20 50 CT KARAT LID PLAS FLAT F/12-22 OZ 5.75 7 1.94 20 000000 0000 C-KCL90 461672 05 7661388 00000000000000 260402 04671945
28 EEK 050 00175469 850081745 DET 1 1 0 22.32 CA 22.32 Y 0 0 0 0 08 60 99 99 PAPER & DISP 24 250 CT ELEMEN NAPKIN 2PLY INTR FOLD 6.3X8.26 16.1 16.8 1.55 24 000000 0000 11904 613310 01 7452585 00000000000000 260402 04672959
29 EEK 050 00175469 850081745 DET 1 1 0 26.15 CA 26.15 Y 0 0 0 0 08 21 64 62 PAPER & DISP 50 50CT KARAT CUP PORTION PLAS CLR 1.50 OZ 10 10 1.36 50 000000 0000 FP-P150-PP 461672 05 4613026 00000000000000 260402 04672959
30 EEK 050 00175469 850081745 DET 1 1 0 43.99 CA 43.99 Y 0 0 0 0 08 75 03 04 PAPER & DISP 6 50 EA NATZWAY BOWL PLASTIC COATING 42 OZ 14.55 17.19 3.56 6 000000 0000 10205 773772 01 7408008 00000000000000 260402 04672959
31 EEK 050 00175469 850081745 DET 1 1 0 41.25 CA 41.25 Y 0 0 0 0 08 42 99 99 PAPER & DISP 6 50CT NATZWAY LID CLEAR PET 42 OZ 8.59 10.47 2.01 6 000000 0000 10206 773772 01 7408215 00000000000000 260402 04672959
32 EEK 050 00175469 850081745 DET 1 1 0 47.6 CA 47.6 Y 0 0 0 0 08 21 56 99 PAPER & DISP 1000 22 OZ NICKGRK CUP PAPER COLD 22 OZ LOGO NTG 31.96 34.62 3.78 1000 000000 0000 810161542703 461672 05 7354127 00000000000000 260402 04672959
33 EEK 050 00175469 850081745 DET 1 1 0 30.6 CA 30.6 Y 0 0 0 0 08 18 56 99 PAPER & DISP 1 450 CT NICKGRK CONTAINER PAPER 1/30 OZ NTG 23 25 3.25 1 000000 0000 810161542673 461672 05 7354120 00000000000000 260402 04672959
34 EEK 050 00175469 850081745 DET 1 1 0 27.6 CA 27.6 Y 0 0 0 0 08 18 56 99 PAPER & DISP 1 160 CT NICKGRK CONTAINER PAPER 4/110OZ NTG 19.6 21.4 3.59 1 000000 0000 810161542680 461672 05 7354119 00000000000000 260402 04672959
35 EEK 050 00175469 850081745 DET 3 3 0 37.6 CA 112.8 Y 0 0 0 0 08 18 02 12 PAPER & DISP 2 100CT NATZWAY CONTAINER PAPER MLD FBR 9X6 18 18 1.28 2 000000 0000 10042 773772 01 7250678 00000000000000 260402 04672959
36 EEK 050 00175469 850081745 DET 2 2 0 46.07 CA 92.14 Y 0 0 0 0 08 09 56 99 PAPER & DISP 1 250BAG NICKGRK BAG PAPER 250 CT 14.5 15 2.18 1 000000 0000 773772 01 7417242 00000000000000 260402 04672959
37 EEK 050 00175469 850081745 DET 1 1 1 0 24.46 EA 24.46 Y 0 0 0 0 08 36 56 79 PAPER & DISP 5 1000 BAGCRFT WRAP DELI WHT 12X12 GRS RESIST 37 37 1.07 5 000000 0000 P057012 276 01 5723808 00000000000000 260402 04672959
38 EEK 050 00175469 850081745 DET 1 1 0 39.41 CA 39.41 Y 3.85 0 0 0 08 06 25 10 PAPER & DISP 10 100CT DHGPROF GLOVE NITRILE BLK PEDRFREE LRG 12.21 12.21 0.66 10 000000 0000 DNGB-L 613310 01 7296407 00000000000000 260402 04672959
39 EEK 050 00175469 850081745 DET 3 3 0 89.32 CA 267.96 Y 0 0 0 0 05 01 01 07 POULTRY 4 10 LB SYS CLS CHICKEN CVP THIGH BNLS SKLS 40 42 1.04 4 000000 0000 14301 3254 21 7792187 00000000000000 260402 04672959
40 EEK 050 00175469 850081745 DET 9 9 0 86.97 CA 782.73 Y 0 0 0 0 05 02 01 99 POULTRY 1 20LB GRECDEL GYRO CHICKEN SHAWARMA CONE 20 21 0.77 1 000000 0000 ME000102 1533 05 7124188 00000000000000 260402 04672959
41 EEK 050 00175469 850081745 DET 5 5 0 23.65 CA 118.25 Y 0 0 0 0 11 02 23 01 PRODUCE 1 50 LB PACKER POTATO KENNEBEC FRESH 50 52 2 1 000000 0000 696760 01 2039220 00000000000000 260402 04672959
42 EEK 050 00175469 850081745 HDR CKC CONCORD INC 260402 Rolling 8 NICK THE GREEK CONCORD 2075 DIAMOND BLVD STE H-103 CONCORD CA 94520-582 408593 000000000 BBNKG 0 050 SYSCO SAN FRANCISCO 5900 STEWART AVENU FREMONT CA 94538 1372486 4024 004 0000000 00000000 20260529 6.25 CRO8
43 EEK 050 00175469 850081745 SUM 40 0 0 74 0 4625.36 6.25 00000463161

View File

@@ -0,0 +1,271 @@
;; =====================================================================
;; ONE-OFF SCRATCH — re-code already-imported Sysco invoices after fixing
;; resources/sysco_line_item_mapping.csv.
;;
;; Context: the Sysco importer codes each line item by EXACT description
;; match against sysco_line_item_mapping.csv, defaulting to GL 50000 when a
;; description is missing (auto-ap.jobs.sysco/get-line-account). Missing
;; PAPER & DISP (and other) descriptions landed in 50000 (Food Costs)
;; instead of their real accounts (e.g. 55000 Paper Costs). The mapping is
;; now fixed; this re-derives the correct split from each invoice's source
;; CSV and rewrites :invoice/expense-accounts.
;;
;; Design:
;; - Recode EVERY invoice found in the CSV resource (a Sysco file may batch
;; several invoices; they're grouped by InvoiceNumber).
;; - Build ONE transaction covering every invoice, emitting only the datoms
;; that actually change:
;; * reuse an existing invoice-expense-account when its account (and
;; location) already match, updating just :amount when it differs;
;; * add a child for an account that has no row yet;
;; * retract a child whose account is no longer in the corrected split;
;; * emit nothing for rows already correct.
;; - Validate with (dc/with db changes): apply the tx to an in-memory db
;; value and assert every affected invoice's expense-account amounts sum
;; to its :invoice/total BEFORE committing for real.
;; - After committing, touch the ledger for every affected invoice. This is
;; a SEPARATE transaction on purpose: :upsert-invoice rebuilds the journal
;; entry from the invoice's expense-accounts as seen in db-before, so it
;; must run after the recode is committed.
;;
;; DO NOT load/evaluate this whole file. Step through the (comment ...) forms
;; one at a time in a connected REPL; the commit + ledger steps are gated #_.
;;
;; PRECONDITIONS
;; - The deployed artifact ships the fixed sysco_line_item_mapping.csv AND
;; the invoice CSV at resources/sysco_recode/<file>.csv (io/resource).
;; - You are connected to the DB you intend to mutate (prod conn!).
;; =====================================================================
(comment
(require '[auto-ap.jobs.sysco :as sysco]
'[auto-ap.datomic :refer [conn audit-transact random-tempid]]
'[auto-ap.utils :refer [dollars=]]
'[auto-ap.time :as t]
'[clj-time.coerce :as coerce]
'[clojure.data.csv :as csv]
'[clojure.java.io :as io]
'[datomic.api :as dc])
;; ------------------------------------------------------------------
;; STEP 0 — reload the mapping cache so the corrected CSV is in effect.
;; ------------------------------------------------------------------
(reset! sysco/sysco-name->line nil)
(count (sysco/get-sysco->line))
;; sanity: a previously-missing paper description now resolves to 55000.
(dc/pull (dc/db conn) [:account/numeric-code :account/name]
(sysco/get-line-account "BAG PAPER 250 CT")) ; => 55000
;; ------------------------------------------------------------------
;; Helpers
;; ------------------------------------------------------------------
(defn read-csv-rows
"Reads the invoice CSV from the classpath (so it ships with the deploy).
`resource-path` is relative to a resources/ root, e.g. \"sysco_recode/bad.csv\"."
[resource-path]
(with-open [r (io/reader (or (io/resource resource-path)
(throw (ex-info "CSV not found on classpath"
{:resource-path resource-path}))))]
(doall (csv/read-csv r))))
(defn parse-date
"Sysco yyMMdd string -> java.util.Date, the same way the importer stores
:invoice/date (auto-ap.jobs.sysco/extract-invoice-details)."
[yymmdd]
(coerce/to-date (t/parse yymmdd "yyMMdd")))
(defn group-invoices
"Split a (possibly multi-invoice) Sysco CSV into one entry per invoice.
Groups DET/HDR/SUM rows by InvoiceNumber (index 4); date comes from the
group's HDR row InvoiceDate (index 10)."
[rows]
(->> rows
(filter #(contains? #{"DET" "HDR" "SUM"} (nth % 5 nil)))
(group-by #(nth % 4))
(mapv (fn [[number grp]]
(let [hdr (first (filter #(= "HDR" (nth % 5)) grp))]
{:invoice-number number
:date-str (some-> hdr (nth 10))
:rows grp})))))
(defn desired-split
"Rows of one Sysco invoice -> {account-eid -> amount-double}, using the
CURRENT (fixed) mapping. DET rows only (record-type at index 5); tax
(SUM row, TotalTaxAmount index 14) added to the same account the
importer uses for \"TAX\". Mirrors auto-ap.jobs.sysco/code-individual-items."
[rows]
(let [det (filter #(= "DET" (nth % 5)) rows)
sum-row (first (filter #(= "SUM" (nth % 5)) rows))
tax (some-> sum-row (nth 14) Double/parseDouble)
by-acct (reduce
(fn [acc row]
(update acc
(sysco/get-line-account (nth row sysco/item-name-index))
(fnil + 0.0)
(Double/parseDouble (nth row sysco/item-price-index))))
{}
det)]
(cond-> by-acct
(and tax (not (zero? tax)))
(update (sysco/get-line-account "TAX") (fnil + 0.0) tax))))
(defn resolve-eid
"Match on invoice-number AND date (belt-and-suspenders). Asserts a unique
hit so we never recode the wrong invoice."
[invoice-number date]
(let [ids (mapv first (dc/q '[:find ?i :in $ ?n ?d
:where
[?i :invoice/invoice-number ?n]
[?i :invoice/date ?d]]
(dc/db conn) invoice-number date))]
(assert (>= 1 (count ids))
(str "multiple invoices match " invoice-number " / " date ": " ids))
(first ids)))
(defn invoice-change-datoms
"Minimal tx-data to make invoice `eid`'s expense-account split equal
`desired` ({account-eid -> amount}). Returns [] when already correct."
[db eid desired]
(let [existing (:invoice/expense-accounts
(dc/pull db [{:invoice/expense-accounts
[:db/id :invoice-expense-account/amount
:invoice-expense-account/location
{:invoice-expense-account/account [:db/id]}]}]
eid))
loc (or (some :invoice-expense-account/location existing) "HQ")
;; one child per account expected; index by account, retract any dupes
by-acct (group-by #(get-in % [:invoice-expense-account/account :db/id]) existing)
one (into {} (map (fn [[a cs]] [a (first cs)])) by-acct)
dupes (mapcat (fn [[_ cs]] (map :db/id (rest cs))) by-acct)
wanted (set (keys desired))
upserts (keep (fn [[acct amt]]
(let [child (get one acct)]
(cond
;; new account -> accrete a child under the invoice
(nil? child)
{:db/id eid
:invoice/expense-accounts
[#:invoice-expense-account{:db/id (random-tempid)
:account acct
:location loc
:amount amt}]}
;; right account, wrong value -> reuse, set amount
;; (and fix location if it drifted)
(or (not (dollars= (:invoice-expense-account/amount child) amt))
(not= (:invoice-expense-account/location child) loc))
(cond-> {:db/id (:db/id child)
:invoice-expense-account/amount amt}
(not= (:invoice-expense-account/location child) loc)
(assoc :invoice-expense-account/location loc))
;; already correct -> nothing
:else nil)))
desired)
retracts (for [[acct child] one :when (not (wanted acct))]
[:db/retractEntity (:db/id child)])]
(vec (concat upserts
retracts
(map (fn [id] [:db/retractEntity id]) dupes)))))
;; ------------------------------------------------------------------
;; STEP 1 — point at the CSV. EVERY invoice in this file gets recoded.
;; Place the file under resources/ (e.g. resources/sysco_recode/bad.csv)
;; and commit it so it's on the classpath of the deployed artifact.
;; ------------------------------------------------------------------
(def csv-path "sysco_recode/bad.csv")
(def rows (read-csv-rows csv-path))
(def invoices (group-invoices rows))
(mapv (juxt :invoice-number :date-str) invoices) ;; what we found in the file
;; ------------------------------------------------------------------
;; STEP 2 — resolve each invoice (number + date) and compute its split.
;; ------------------------------------------------------------------
(def plan
(mapv (fn [{:keys [invoice-number date-str rows]}]
(let [date (parse-date date-str)]
{:invoice-number invoice-number
:date date
:eid (resolve-eid invoice-number date)
:desired (desired-split rows)}))
invoices))
;; bail if any invoice number didn't resolve
(assert (every? :eid plan)
(str "unresolved invoices: "
(mapv (juxt :invoice-number :date) (remove :eid plan))))
;; ------------------------------------------------------------------
;; STEP 3 — build the SINGLE changes-only transaction across all invoices.
;; ------------------------------------------------------------------
(def changes
(let [db (dc/db conn)]
(vec (mapcat (fn [{:keys [eid desired]}] (invoice-change-datoms db eid desired))
plan))))
(count changes) ;; how many datoms we're actually changing
changes ;; inspect the full minimal tx
;; ------------------------------------------------------------------
;; STEP 4 — validate with dc/with: apply the tx to an in-memory db value
;; and confirm every affected invoice still balances (sum of expense
;; account amounts == :invoice/total).
;; ------------------------------------------------------------------
(def preview (dc/with (dc/db conn) changes))
(def balance-report
(let [db-after (:db-after preview)]
(mapv (fn [{:keys [eid invoice-number]}]
(let [inv (dc/pull db-after
[:invoice/total
{:invoice/expense-accounts [:invoice-expense-account/amount]}]
eid)
s (reduce + 0.0 (map :invoice-expense-account/amount
(:invoice/expense-accounts inv)))]
{:invoice-number invoice-number
:total (:invoice/total inv)
:ea-sum s
:ok? (dollars= s (:invoice/total inv))}))
plan)))
balance-report
;; HARD GATE — do not continue unless every invoice balances post-change.
(assert (every? :ok? balance-report)
(str "unbalanced after change: "
(filterv (complement :ok?) balance-report)))
;; ------------------------------------------------------------------
;; STEP 5 — COMMIT the recode (gated). One transaction, changes only.
;; ------------------------------------------------------------------
#_(audit-transact changes
{:user/name "sysco recode (missing GL mappings fix)"
:user/role "admin"})
;; ------------------------------------------------------------------
;; STEP 6 — touch the ledger for every affected invoice (separate tx;
;; :upsert-invoice rebuilds the journal entry from the now-committed
;; expense-accounts). Gated.
;; ------------------------------------------------------------------
#_(audit-transact (mapv (fn [{:keys [eid]}] [:upsert-invoice {:db/id eid}]) plan)
{:user/name "sysco recode ledger touch"
:user/role "admin"})
;; ------------------------------------------------------------------
;; STEP 7 — verify committed result.
;; ------------------------------------------------------------------
#_(let [db (dc/db conn)]
(mapv (fn [{:keys [eid invoice-number]}]
{:invoice-number invoice-number
:accounts
(->> (dc/pull db
[{:invoice/expense-accounts
[:invoice-expense-account/amount
{:invoice-expense-account/account [:account/numeric-code]}]}]
eid)
:invoice/expense-accounts
(map (juxt #(get-in % [:invoice-expense-account/account :account/numeric-code])
:invoice-expense-account/amount))
(sort-by first)
vec)})
plan)))

View File

@@ -0,0 +1,375 @@
(ns auto-ap.jobs.backfill-olo-processors
"One-off backfill for the Olo third-party-source fix (commit fa25620b, \"olo fixes\").
That commit replaced the exact-match `condp` on `(:name (:source order))` in
`square3/tender->charge` with substring matching, so Olo-brokered orders that
arrive from Square with source names like \"Olo - DoorDash\" or
\"olo-ubereats\" now classify as the real delivery processor instead of
falling through to `:ccp-processor/na`.
Orders imported *before* the fix shipped still carry the old
`:charge/processor`, which means `sales-summaries` bucketed them as
\"Unknown\" instead of \"Food App Payments\". This job recomputes
`:charge/processor` for already-imported Square charges over a date range
(default: 2026-07-07 -> today) and marks the affected sales summaries dirty
so `auto-ap.jobs.sales-summaries` recalculates them.
IMPORTANT: this backfill never re-implements the classifier. It feeds the
stored shape of each charge back through the real production function
(`square3/tender->charge`), so it cannot drift from the importer.
No Square API calls are made -- every input the classifier needs
(`:sales-order/source`, `:charge/note`, `:charge/type-name`) is already in
Datomic. See the `(comment ...)` block at the bottom for the API-based
fallback if you need to repair charges that have no parent sales order.
Dry run by default. Pass `:apply? true` to write."
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.jobs.core :refer [execute]]
[auto-ap.jobs.sales-summaries :as summaries]
[auto-ap.logging :as alog]
[auto-ap.square.core3 :as square3]
[auto-ap.time :as atime]
[clj-time.coerce :as coerce]
[clj-time.core :as time]
[clj-time.format :as f]
[clj-time.periodic :as per]
[clojure.string :as str]
[config.core :refer [env]]
[datomic.api :as dc]
;; the datalog below calls iol-ion.query/scan-charges by fully-qualified
;; symbol, so the namespace has to be loaded.
[iol-ion.query]))
;; ---------------------------------------------------------------------------
;; Date handling
;; ---------------------------------------------------------------------------
(def pacific (time/time-zone-for-id "America/Los_Angeles"))
(def default-start-date
"The Olo fix landed 2026-07-22; the user-visible bad data starts 7/7."
"2026-07-07")
(defn parse-day
"\"2026-07-07\" -> local (Pacific) midnight DateTime. Passes DateTimes through."
[d]
(cond
(nil? d) nil
(string? d) (f/parse (f/with-zone (f/formatter "yyyy-MM-dd") pacific) d)
:else (coerce/to-date-time d)))
(defn local-midnight [dt]
(.toDateMidnight (atime/localize dt)))
;; ---------------------------------------------------------------------------
;; Classification -- delegates to the production importer
;; ---------------------------------------------------------------------------
(defn recompute-processor
"Runs the *live* classifier over a charge's stored inputs.
`tender->charge` only reads `(:name (:source order))` from the order and
`:type`/`:note` from the tender when deciding `:charge/processor`, so a
synthetic order/tender carrying the stored values yields exactly what a
fresh import would produce today. `:created_at` and `:id` are placeholders
purely to keep the unrelated `:charge/date` and `:charge/reference-link`
branches from blowing up; we only read `:charge/processor` back out.
Note on nil sources: the importer stores `(or (:name (:source order))
\"Square\")`, so a source that was originally nil comes back as \"Square\".
Both nil and \"Square\" classify to `:ccp-processor/na`, so the round trip
is faithful."
[{:keys [source note type-name]}]
(:charge/processor
(square3/tender->charge {:source {:name source}
:created_at "1970-01-01T00:00:00Z"}
{} ; client -> only :charge/client
{} ; location -> only :charge/location
{:id "backfill"
:type type-name
:note note})))
;; ---------------------------------------------------------------------------
;; Scanning
;; ---------------------------------------------------------------------------
(def charge-pull
[:db/id
:charge/external-id
:charge/type-name
:charge/note
:charge/date
:charge/total
{:charge/processor [:db/ident]}
{:sales-order/_charges [:db/id
:sales-order/external-id
:sales-order/source
{:sales-order/vendor [:db/ident]}]}])
(defn client-charges
"All charges for `client-eid` between `start-inst` and `end-inst` (both
inclusive by day), via the :charge/client+date index."
[db client-eid start-inst end-inst]
(->> (dc/q '[:find (pull ?c pull-pattern)
:in $ pull-pattern [?clients ?start ?end]
:where
[(iol-ion.query/scan-charges $ ?clients ?start ?end) [[?c _ _] ...]]]
db
charge-pull
[[client-eid] start-inst end-inst])
(map first)))
(defn parent-order
"Reverse refs on component attributes come back as a single map from pull,
but tolerate a collection in case that ever changes."
[charge]
(let [o (:sales-order/_charges charge)]
(if (sequential? o) (first o) o)))
(defn square-charge? [charge]
(= :vendor/ccp-square (get-in (parent-order charge) [:sales-order/vendor :db/ident])))
;; ---------------------------------------------------------------------------
;; Planning
;; ---------------------------------------------------------------------------
(defn charge->change
"nil when the charge is already correct (or isn't ours to touch)."
[charge]
(when (square-charge? charge)
(let [order (parent-order charge)
current (get-in charge [:charge/processor :db/ident])
next (recompute-processor {:source (:sales-order/source order)
:note (:charge/note charge)
:type-name (:charge/type-name charge)})]
(when (not= current next)
{:charge (:db/id charge)
:external-id (:charge/external-id charge)
:order (:sales-order/external-id order)
:date (:charge/date charge)
:source (:sales-order/source order)
:note (:charge/note charge)
:type-name (:charge/type-name charge)
:total (:charge/total charge)
:from current
:to next}))))
(defn downgrade?
"A change that *loses* processor information. The Olo fix can only ever widen
matching, so these should not exist -- if they do, something else changed
and we'd rather report than clobber."
[{:keys [from to]}]
(and (= :ccp-processor/na to)
(not= :ccp-processor/na from)
(some? from)))
(defn plan-for-client
[db {:keys [db/id client/code]} start-inst end-inst allow-downgrade?]
(let [charges (client-charges db id start-inst end-inst)
square (filter square-charge? charges)
orphans (->> charges
(remove (comp some? parent-order))
(filter #(some-> (:charge/external-id %)
(str/starts-with? "square/charge/"))))
all (keep charge->change square)
[skipped changes] (if allow-downgrade?
[[] all]
[(filter downgrade? all) (remove downgrade? all)])]
{:client id
:code code
:scanned (count square)
:orphan-charges (count orphans)
:changes (vec changes)
:skipped (vec skipped)}))
(defn build-plan
[db clients start-inst end-inst allow-downgrade?]
(->> clients
(map #(plan-for-client db % start-inst end-inst allow-downgrade?))
(remove #(and (zero? (count (:changes %)))
(zero? (count (:skipped %)))
(zero? (:orphan-charges %))))
vec))
;; ---------------------------------------------------------------------------
;; Reporting
;; ---------------------------------------------------------------------------
(defn- transitions [changes]
(->> changes
(group-by (juxt :from :to))
(map (fn [[[from to] cs]]
[from to (count cs) (reduce + 0.0 (keep :total cs))]))
(sort-by #(- (nth % 2)))))
(defn print-report [plan]
(println)
(println "=== Olo processor backfill ===")
(doseq [{:keys [code scanned changes skipped orphan-charges]} plan]
(println)
(printf "%-8s scanned %d square charges, %d to update%n"
(str code) scanned (count changes))
(doseq [[from to n total] (transitions changes)]
(printf " %-24s -> %-24s %5d $%.2f%n"
(str from) (str to) n total))
(when (seq skipped)
(printf " !! %d downgrade(s) to :ccp-processor/na SKIPPED (pass :allow-downgrade? true to force)%n"
(count skipped))
(doseq [{:keys [external-id order source note from]} (take 10 skipped)]
(printf " %s (order %s, source %s, note %s) was %s%n"
external-id order (pr-str source) (pr-str note) from)))
(when (pos? orphan-charges)
(printf " note: %d square charge(s) have no parent sales order (custom-amount tenders).%n"
orphan-charges)
(println " Their order source is not stored, so they cannot be reclassified")
(println " locally -- use the re-import fallback in the comment block.")))
(let [total (reduce + 0 (map (comp count :changes) plan))]
(println)
(printf "TOTAL: %d charge(s) across %d client(s)%n" total (count plan))
(println)
total))
;; ---------------------------------------------------------------------------
;; Applying
;; ---------------------------------------------------------------------------
(defn apply-plan!
"Transacts the processor corrections in batches. Returns the number written."
[plan]
(let [tx (for [{:keys [changes]} plan
{:keys [charge to]} changes]
{:db/id charge :charge/processor to})]
(doseq [batch (partition-all 100 tx)]
(alog/info ::updating-charges :count (count batch))
@(dc/transact-async conn batch))
(count tx)))
(defn mark-summaries-dirty!
"Processor drives the Card / Food App / Unknown split in
`sales-summaries/get-payment-items`, so every day we touched has to be
recalculated. `periodic-seq` is end-exclusive, hence the +1 day."
[plan start end]
(doseq [{:keys [client code changes]} plan
:when (seq changes)]
(alog/info ::marking-summaries-dirty :client code)
(summaries/mark-dirty client
(local-midnight start)
(time/plus (local-midnight end) (time/days 1)))))
;; ---------------------------------------------------------------------------
;; Entry point
;; ---------------------------------------------------------------------------
(defn backfill!
"Recompute :charge/processor for imported Square charges.
Options:
:start \"2026-07-07\" (default) or a DateTime -- inclusive
:end \"2026-07-29\" or a DateTime -- inclusive, default today
:codes seq of client codes; default every Square client
:apply? false (default) = dry run, print only
:allow-downgrade? false (default) = refuse changes that drop a known
processor back to :ccp-processor/na
:mark-dirty? true (default when applying) = flag sales summaries for
recalculation
Returns the plan so you can inspect individual charges in the REPL."
[& {:keys [start end codes apply? allow-downgrade? mark-dirty?]
:or {start default-start-date
apply? false
allow-downgrade? false}}]
(let [start-dt (parse-day start)
end-dt (or (parse-day end) (atime/localize (time/now)))
start-inst (coerce/to-date (local-midnight start-dt))
end-inst (coerce/to-date (local-midnight end-dt))
db (dc/db conn)
clients (if (seq codes)
(apply square3/get-square-clients codes)
(square3/get-square-clients))
_ (alog/info ::scanning
:start start-inst
:end end-inst
:clients (count clients)
:dry-run? (not apply?))
plan (build-plan db clients start-inst end-inst allow-downgrade?)
total (print-report plan)]
(if-not apply?
(do (println "DRY RUN -- nothing written. Re-run with :apply? true to commit.")
(alog/info ::dry-run-complete :change-count total))
(do
(alog/info ::applying :change-count total)
(apply-plan! plan)
(when (not= false mark-dirty?)
(mark-summaries-dirty! plan start-dt end-dt)
(println "Sales summaries marked dirty. Run auto-ap.jobs.sales-summaries")
(println "(or `(auto-ap.jobs.sales-summaries/sales-summaries-v2)`) to rebuild them."))
(alog/info ::done :change-count total)))
plan))
(defn -main [& _]
(execute "backfill-olo-processors"
(fn []
(let [{:keys [start end codes apply allow-downgrade mark-dirty]} (:args env)]
(backfill! :start (or start default-start-date)
:end end
:codes (cond-> codes (string? codes)
(str/split #","))
:apply? (boolean apply)
:allow-downgrade? (boolean allow-downgrade)
:mark-dirty? (if (nil? mark-dirty) true (boolean mark-dirty)))))))
(comment
;; ---------------------------------------------------------------------
;; 1. Dry run everything from 7/7 -- read only, prints what would change.
;; ---------------------------------------------------------------------
(def plan (backfill!))
;; One client at a time while you sanity check.
(backfill! :codes ["NGCL"])
;; Eyeball the actual charges behind a transition.
(->> plan
(mapcat :changes)
(filter #(= :ccp-processor/doordash (:to %)))
(map (juxt :date :source :note :total :from :to))
(take 20))
;; Every distinct source string we saw reclassified -- confirms the Olo
;; variants ("Olo - DoorDash", "olo-ubereats", ...) are what moved.
(->> plan (mapcat :changes) (map :source) frequencies)
;; ---------------------------------------------------------------------
;; 2. Commit, then rebuild the summaries.
;; ---------------------------------------------------------------------
(backfill! :apply? true)
(auto-ap.jobs.sales-summaries/sales-summaries-v2)
;; ---------------------------------------------------------------------
;; 3. Verify: no square charge in the window disagrees with the classifier.
;; ---------------------------------------------------------------------
(->> (backfill!) (mapcat :changes) count) ; => 0
;; ---------------------------------------------------------------------
;; Fallback: charges with no parent sales order (custom-amount tenders,
;; see `is-order-only-for-charge?` in square3/order->sales-order) do not
;; store the order source, so they cannot be fixed locally. Re-import them
;; through the real pipeline instead -- `upsert` is idempotent on
;; :charge/external-id / :sales-order/external-id, so re-running a day is
;; safe and rewrites the processor from live Square data.
;;
;; This hits the Square API for every day x location; the existing
;; auto-ap.jobs.load-historical-sales job does the same thing by day count.
;; ---------------------------------------------------------------------
(doseq [client (square3/get-square-clients)
location (:client/square-locations client)
:when (:square-location/client-location location)
d (per/periodic-seq (parse-day "2026-07-07")
(time/plus (atime/localize (time/now)) (time/days 1))
(time/days 1))]
(println (:client/code client) (:square-location/client-location location) (str d))
@(square3/upsert client location d (time/plus d (time/days 1))))
;; ...then mark dirty + rebuild summaries as in step 2.
)

View File

@@ -35,19 +35,35 @@
(into {})))))) (into {}))))))
@sysco-name->line) @sysco-name->line)
(defn get-line-account [item-name] (defn get-account-by-code [numeric-code]
(get (get-sysco->line) (ffirst (dc/q '[:find ?a
item-name :in $ ?an
(ffirst (dc/q '[:find ?a :where [?a :account/numeric-code ?an]]
:in $ ?an (dc/db conn)
:where [?a :account/numeric-code ?an]] numeric-code)))
(dc/db conn)
50000)))) ;; Sysco categories whose *default* is unambiguous, so a line item whose
;; description is missing from sysco_line_item_mapping.csv still codes
;; correctly instead of silently defaulting to Food Costs. Only PAPER & DISP
;; qualifies: 440 of its 455 mapped descriptions point at 55000, and the 15
;; exceptions (foil pans -> 51500, scour pads -> 74100) are all mapped
;; explicitly, so the description mapping wins ahead of this fallback.
(def category->numeric-code {"PAPER & DISP" 55000})
(def default-numeric-code 50000)
(defn get-line-account
([item-name] (get-line-account item-name nil))
([item-name sysco-category]
(or (get (get-sysco->line) item-name)
(some-> (category->numeric-code sysco-category) get-account-by-code)
(get-account-by-code default-numeric-code))))
(def ^:dynamic bucket-name (:data-bucket env)) (def ^:dynamic bucket-name (:data-bucket env))
(def header-keys ["TransCode" "GroupID" "Company" "CustomerNumber" "InvoiceNumber" "RecordType" "Item" "InvoiceDocument" "AccountName" "AccountDunsNo" "InvoiceDate" "AccountDate" "CustomerPONo" "PaymentTerms" "TermsDescription" "StoreNumber" "CustomerName" "AddressLine1" "AddressLine2" "City1" "State1" "Zip1" "Phone1" "Duns1" "Hin1" "Dea1" "TIDCustomer" "ChainNumber" "BidNumber" "ContractNumber" "CompanyNumber" "BriefName" "Address" "Address2" "City2" "State2" "Zip2" "Phone2" "Duns2" "Hin2" "Dea2" "Tid_OPCO" "ObligationIndicator" "Manifest" "Route" "Stop" "TermsDiscountPercent" "TermsDiscountDueDate" "TermsNetDueDate" "TermsDiscountAmount" "TermsDiscountCode" "OrderDate" "DepartmentCode"]) (def header-keys ["TransCode" "GroupID" "Company" "CustomerNumber" "InvoiceNumber" "RecordType" "Item" "InvoiceDocument" "AccountName" "AccountDunsNo" "InvoiceDate" "AccountDate" "CustomerPONo" "PaymentTerms" "TermsDescription" "StoreNumber" "CustomerName" "AddressLine1" "AddressLine2" "City1" "State1" "Zip1" "Phone1" "Duns1" "Hin1" "Dea1" "TIDCustomer" "ChainNumber" "BidNumber" "ContractNumber" "CompanyNumber" "BriefName" "Address" "Address2" "City2" "State2" "Zip2" "Phone2" "Duns2" "Hin2" "Dea2" "Tid_OPCO" "ObligationIndicator" "Manifest" "Route" "Stop" "TermsDiscountPercent" "TermsDiscountDueDate" "TermsNetDueDate" "TermsDiscountAmount" "TermsDiscountCode" "OrderDate" "DepartmentCode"])
(def item-price-index 15) (def item-price-index 15)
(def item-category-index 25)
(def item-name-index 29) (def item-name-index 29)
(def summary-keys ["TranCode" "GroupID" "Company" "CustomerNumber" "InvoiceNumber" "RecordType" "Item" "InvoiceDocument" "TotalLines" "TotalQtyInvoice" "TotalQty" "TotalQtySplit" "TotalQtyPounds" "TotalExtendedPrice" "TotalTaxAmount" "TotalInvoiceAmount" "AccountDate"]) (def summary-keys ["TranCode" "GroupID" "Company" "CustomerNumber" "InvoiceNumber" "RecordType" "Item" "InvoiceDocument" "TotalLines" "TotalQtyInvoice" "TotalQty" "TotalQtySplit" "TotalQtyPounds" "TotalExtendedPrice" "TotalTaxAmount" "TotalInvoiceAmount" "AccountDate"])
@@ -80,7 +96,8 @@
butlast butlast
(reduce (reduce
(fn [acc row] (fn [acc row]
(update acc (get-line-account (nth row item-name-index)) (update acc (get-line-account (nth row item-name-index)
(nth row item-category-index))
(fnil + 0.0) (fnil + 0.0)
(Double/parseDouble (nth row item-price-index)))) (Double/parseDouble (nth row item-price-index))))

View File

@@ -315,21 +315,55 @@
[[(:db/id a) (:db/id (:account-client-override/client o))] [[(:db/id a) (:db/id (:account-client-override/client o))]
(:account-client-override/name o)]) (:account-client-override/name o)])
(:account/client-overrides a)))) (:account/client-overrides a))))
(into {}))] (into {}))
;; A client's bank account and the financial account it posts to share a
;; numeric code. Reports key their rows off that code, so the two would
;; otherwise render as two rows for the same account. Resolve every
;; account at a shared code down to the bank account's name. Where a
;; client has two bank accounts on one code (a data-entry error, but it
;; happens) the lowest :bank-account/sort-order wins, then the lowest
;; :db/id, so the name is stable across runs.
bank-name-by-code (->> (dc/q {:find ['(pull ?b [:db/id :bank-account/name
:bank-account/numeric-code
:bank-account/sort-order])]
:in ['$ '?c]
:where ['[?c :client/bank-accounts ?b]]}
(dc/db conn)
client-id)
(map first)
(filter (every-pred :bank-account/numeric-code :bank-account/name))
(sort-by (juxt #(or (:bank-account/sort-order %) Long/MAX_VALUE)
:db/id))
(reduce (fn [m b]
(let [code (:bank-account/numeric-code b)]
(cond-> m
(not (contains? m code))
(assoc code (:bank-account/name b)))))
{}))]
(fn [a] (fn [a]
{:name (or (:bank-account/name (bank-accounts a)) (let [account (accounts a)
(overrides-by-client [a client-id]) bank-account (bank-accounts a)
(:account/name (accounts a))) numeric-code (or (:account/numeric-code account)
:account_type (or (:db/ident (:account/type (accounts a))) (:bank-account/numeric-code bank-account))
({:bank-account-type/check :account-type/asset bank-name (or (bank-name-by-code numeric-code)
:bank-account-type/cash :account-type/asset (:bank-account/name bank-account))]
:bank-account-type/credit :account-type/liability} {:name (or bank-name
(:db/ident (:bank-account/type (bank-accounts a)))) (overrides-by-client [a client-id])
:account-type/asset ;; DEFAULT TO ASSET, for things like unknown (:account/name account))
) ;; Whether :name above came from a bank account. Reports that pool
:numeric_code (or (:account/numeric-code (accounts a)) ;; several clients into one row per code use this to pick the label,
(:bank-account/numeric-code (bank-accounts a))) ;; since only some of those clients may have a bank account at the code.
:client_id client-id}))) :bank_account_name? (some? bank-name)
:account_type (or (:db/ident (:account/type account))
({:bank-account-type/check :account-type/asset
:bank-account-type/cash :account-type/asset
:bank-account-type/credit :account-type/liability}
(:db/ident (:bank-account/type bank-account)))
:account-type/asset ;; DEFAULT TO ASSET, for things like unknown
)
:numeric_code numeric-code
:client_id client-id}))))
(defn find-mismatch-index [] (defn find-mismatch-index []
(reduce + 0 (reduce + 0

View File

@@ -742,6 +742,19 @@
:total [:trim-commas-and-negate nil]} :total [:trim-commas-and-negate nil]}
:multi #"\n" :multi #"\n"
:multi-match? #"^\d+"} :multi-match? #"^\d+"}
;; REEL PRODUCE STATEMENT (QuickBooks layout -- no "Reel Produce" text on the page)
{:vendor "Reel Produce"
:keywords [#"reelproduce\.com" #"Statement"]
:extract {:date #"^\s*([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"To:(?:.*?)\n\s*(.*?)\s{2,}"
:invoice-number #"INV #(\d+)"
:total #"Orig\. Amount \$([\d\-,]+\.\d{2,2})"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}
:multi #"\n"
:multi-match? #"^\s*[0-9]+/[0-9]+/[0-9]+\s+INV #"}
{:vendor "Paulino's Bakery" {:vendor "Paulino's Bakery"
:keywords [#"paulinosbakery"] :keywords [#"paulinosbakery"]
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)" :extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
@@ -758,8 +771,22 @@
:keywords [#"530-544-4136"] :keywords [#"530-544-4136"]
:extract {:invoice-number #"NO\s+(\d{8,})\s+\d{2}/\d{2}/\d{2}" :extract {:invoice-number #"NO\s+(\d{8,})\s+\d{2}/\d{2}/\d{2}"
:date #"NO\s+\d{8,}\s+(\d{2}/\d{2}/\d{2})" :date #"NO\s+\d{8,}\s+(\d{2}/\d{2}/\d{2})"
:customer-identifier #"(?s)I\s+([A-Z][A-Z\s]+?)\s{2,}.*?L\s+([0-9][A-Z0-9\s]+?)(?=\s{2,}|\n)" ;; The bill-to block spells BILL TO down the left margin, one letter
:account-number #"(?s)L\s+([0-9][A-Z0-9\s]+?)(?=\s{2,}|\n)" ;; per line, with the customer's details in the column to its right:
;; B NICKGK
;; I NICK THE GREEK <<McCARRAN>>
;; L NICK THE GREEK <<McCARRAN>>
;; L 10310 N McCARRAN BLVD STE.400
;; RENO, NV 89503
;; Anchor on those margin letters at the start of a line -- the ship-to
;; block to the right reuses the same letters mid-line -- and take the
;; whole column up to the next column gap. That column carries mixed
;; case and punctuation, so it must not be restricted to [A-Z0-9\s];
;; doing so dropped store names like McCARRAN entirely.
:customer-identifier #"(?m)^\s+I\s{2,}(\S.*?)(?:\s{2,}|$)"
;; Both the name and the street sit on an L line; the street is the
;; one that starts with a house number.
:account-number #"(?m)^\s+L\s{2,}(\d\S*.*?)(?:\s{2,}|$)"
:total #"SHIPPED\s+[\d\.]+\s+TOTAL\s+([\d\.]+)"} :total #"SHIPPED\s+[\d\.]+\s+TOTAL\s+([\d\.]+)"}
:parser {:date [:clj-time "MM/dd/yy"] :parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas nil]}} :total [:trim-commas nil]}}

View File

@@ -22,17 +22,22 @@
(if (not-empty q) (if (not-empty q)
(->> (->>
(str/split q #",") (str/split q #",")
(map (fn [k] ;; NOTE: matching-header is deliberately not carried in the result. It is the
(let [[key asc?] (str/split k #":") ;; grid's header map, which holds a :render fn. This sort lands in :query-params,
matching-header (first (filter #(= (str key) (:sort-key %)) (:headers grid-spec)))] ;; which the bulk wizards copy into their form snapshot, and that snapshot is
{:sort-key (str key) ;; round-tripped through pr-str / clojure.edn/read-string. A fn pr-strs as
:asc (boolean (= "asc" asc?)) ;; #object[...], which edn has no reader for, 500ing the submit.
:matching-header matching-header ;; apply-toggle-sort below already builds entries without it.
:name (:name matching-header) (keep (fn [k]
:sort-icon (if (= (boolean (= "asc" asc?)) true) (let [[key asc?] (str/split k #":")
svg/sort-down matching-header (first (filter #(= (str key) (:sort-key %)) (:headers grid-spec)))]
svg/sort-up)}))) (when matching-header
(filter :matching-header) {:sort-key (str key)
:asc (boolean (= "asc" asc?))
:name (:name matching-header)
:sort-icon (if (= (boolean (= "asc" asc?)) true)
svg/sort-down
svg/sort-up)}))))
(into [])) (into []))
[])) []))

View File

@@ -281,15 +281,15 @@
"grubhub" :ccp-processor/grubhub "grubhub" :ccp-processor/grubhub
"grub" :ccp-processor/grubhub "grub" :ccp-processor/grubhub
"gh" :ccp-processor/grubhub "gh" :ccp-processor/grubhub
(condp = (:name (:source order)) (let [src (some-> (:name (:source order)) str/lower-case)]
"GRUBHUB" :ccp-processor/grubhub (cond
"UBEREATS" :ccp-processor/uber-eats (nil? src) :ccp-processor/na
"Uber Eats" :ccp-processor/uber-eats (str/includes? src "doordash") :ccp-processor/doordash
"DOORDASH" :ccp-processor/doordash (str/includes? src "uber") :ccp-processor/uber-eats
"DoorDash" :ccp-processor/doordash (str/includes? src "grubhub") :ccp-processor/grubhub
"Koala" :ccp-processor/koala (str/includes? src "postmates") :ccp-processor/uber-eats
"koala-production" :ccp-processor/koala (str/includes? src "koala") :ccp-processor/koala
:ccp-processor/na)) :else :ccp-processor/na)))
(= (:type t) "CARD") (= (:type t) "CARD")
:ccp-processor/square :ccp-processor/square

View File

@@ -112,9 +112,11 @@
true (str " focus:ring-4 font-bold rounded-lg text-xs p-3 text-center mr-2 inline-flex items-center hover:scale-105 transition duration-100 justify-center") true (str " focus:ring-4 font-bold rounded-lg text-xs p-3 text-center mr-2 inline-flex items-center hover:scale-105 transition duration-100 justify-center")
(= :secondary (:color params)) (str " text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700") (= :secondary (:color params)) (str " text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700")
(= :primary (:color params)) (str " text-white bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ") (= :primary (:color params)) (str " text-white bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ")
(= :secondary-light (:color params)) (str " text-blue-800 bg-white-200 border-gray-100 border hover:bg-blue-100 focus:ring-blue-100 dark:bg-blue-400 dark:hover:bg-blue-800 ") (= :secondary-light (:color params)) (str " text-blue-800 bg-blue-100 border-blue-300 border hover:bg-blue-200 focus:ring-blue-100 dark:text-white dark:bg-blue-700 dark:border-blue-500 dark:hover:bg-blue-600 ")
(not (nil? (:color params))) ;; the light variants paint their own text/background above -
;; falling through here would stack white text on them
(not (contains? #{nil :secondary-light} (:color params)))
(str " text-white " (bg-colors (:color params) (:disabled params))) (str " text-white " (bg-colors (:color params) (:disabled params)))
(nil? (:color params)) (nil? (:color params))

View File

@@ -12,9 +12,14 @@
[hiccup2.core :as hiccup])) [hiccup2.core :as hiccup]))
(defn header- [params & rest] (defn header- [params & rest]
(into [:th.px-4.py-3 {:scope "col" :class (:class params) ;; NOTE: only attach the sort dispatcher when there is a :sort-key. Otherwise
"@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params)) ;; (format "%s" nil) renders the literal string "null", which sails through the
:style (:style params)}] ;; thead's `event.detail.key || ""` guard and reaches the server as
;; toggle-sort=null, ending in a Datomic unbound-variable error on ?sort-null.
(into [:th.px-4.py-3 (cond-> {:scope "col" :class (:class params)
:style (:style params)}
(:sort-key params)
(assoc "@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))))]
(if (:sort-key params) (if (:sort-key params)
[(into [:a {:href "#"}] rest)] [(into [:a {:href "#"}] rest)]
rest))) rest)))

View File

@@ -86,6 +86,13 @@
:x-init (hiccup/raw (str "$watch('value', v => { $el.value = (v && v.value != null) ? v.value : ''; $nextTick(() => $dispatch('change')); }); "))))] :x-init (hiccup/raw (str "$watch('value', v => { $el.value = (v && v.value != null) ? v.value : ''; $nextTick(() => $dispatch('change')); }); "))))]
[:div.flex.w-full.justify-items-stretch [:div.flex.w-full.justify-items-stretch
[:span.flex-grow.text-left {"x-text" "value.label"}] [:span.flex-grow.text-left {"x-text" "value.label"}]
[:div {:class "w-4 h-4 m-1 inline ml-1 justify-self-end self-center cursor-pointer text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
:tabindex "-1"
:aria-hidden "true"
:title "Clear selection"
"@click.prevent.stop" "value = {value: '', label: ''}; if (tippy) { tippy.hide(); }"
:x-show "!!(value && value.value)"}
svg/x]
[:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"} [:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
svg/drop-down] svg/drop-down]
[:div {:x-show "value.warning"} [:div {:x-show "value.warning"}

View File

@@ -55,9 +55,9 @@
[:div.flex.flex-col.gap-2 [:div.flex.flex-col.gap-2
(buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending " (buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending "
[:span {:x-text "source_date"}]]) [:span {:x-text "source_date"}]])
(buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} [:span "Calendar year (" (buttons/a-button- {"@click" "periods=[previousCalendarYearPeriod(source_date)]"} [:span "Previous Calendar Year ("
[:span {:x-text "parseMMDDYYYY(source_date).getFullYear()"}] [:span {:x-text "parseMMDDYYYY(source_date).getFullYear() - 1"}]
")"]) ")"])
(buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending " (buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending "
[:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]]) [:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]])
[:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"}] [:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"}]

View File

@@ -149,8 +149,11 @@
main-transformer)) main-transformer))
"sort" sort->query) "sort" sort->query)
"selected" "all-selected")) "selected" "all-selected"))
:color :secondary-light} :color :secondary-light
[:div.w-4.h-4 svg/download]))) :title "Export CSV"
:aria-label "Export CSV"}
[:div.w-4.h-4 svg/download]
"CSV")))
:rows :rows
(let [break-table-fn (some-> grid-spec :break-table (create-break-table-fn grid-spec))] (let [break-table-fn (some-> grid-spec :break-table (create-break-table-fn grid-spec))]
(for [entity entities (for [entity entities

View File

@@ -656,7 +656,7 @@
{:exact-match-id (:db/id p)}) {:exact-match-id (:db/id p)})
:content (str (format "$%,.2f" (:payment/amount p)) :content (str (format "$%,.2f" (:payment/amount p))
(some-> (:payment/date p) coerce/to-date-time (atime/unparse-local atime/normal-date) (#(str " payment on " %))))}] (some-> (:payment/date p) coerce/to-date-time (atime/unparse-local atime/normal-date) (#(str " payment on " %))))}]
(:payment/transaction p) (conj {:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page) (:payment/transaction p) (conj {:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/page)
{:exact-match-id (:db/id (first (:payment/transaction p)))}) {:exact-match-id (:db/id (first (:payment/transaction p)))})
:color :secondary :color :secondary
:content "Transaction"}))))) :content "Transaction"})))))

View File

@@ -148,17 +148,18 @@
(= ::route/external-page matched-current-page-route) (assoc-in [:route-params :external?] true))] (= ::route/external-page matched-current-page-route) (assoc-in [:route-params :external?] true))]
(handler request)))) (handler request))))
(defn line->id [{:keys [source external-id client-code]}]
(str client-code "-" source "-" external-id))
(defn external-import-table-form* [request] (defn external-import-table-form* [request]
[:div#table-form [:div#table-form
(clojure.pprint/pprint (:form-errors request))
(fc/start-form (fc/start-form
(:form-params request) (:form-params request)
(:form-errors request) (:form-errors request)
(fc/with-field :table (fc/with-field :table
(clojure.pprint/pprint (fc/field-errors))
(when (seq (fc/field-value)) (when (seq (fc/field-value))
[:div {:x-data (hx/json {"showTable" false})} [:div {:x-data (hx/json {"showTable" false "errorsOnly" false})}
[:form {:hx-post (bidi.bidi/path-for ssr-routes/only-routes ::route/external-import-import) [:form {:hx-post (bidi.bidi/path-for ssr-routes/only-routes ::route/external-import-import)
:autocomplete "off"} :autocomplete "off"}
(when (:just-parsed? request) (when (:just-parsed? request)
@@ -179,8 +180,10 @@
[:div.flex.gap-4.items-center [:div.flex.gap-4.items-center
(com/checkbox {"@click" "showTable=!showTable"} (com/checkbox {"@click" "showTable=!showTable"}
"Show table") "Show table")
(com/checkbox {:x-model "errorsOnly"}
"Only show errors")
(com/button {:color :primary} "Import")] (com/button {:color :primary} "Import")]
[:div {:x-show "showTable"} [:div {:x-show "showTable || errorsOnly"}
(com/data-grid-card {:id "ledger-import-data" (com/data-grid-card {:id "ledger-import-data"
:route nil :route nil
:title "Data to import" :title "Data to import"
@@ -198,81 +201,100 @@
:rows :rows
(fc/cursor-map (fc/cursor-map
(fn [r] (fn [r]
(com/data-grid-row {} (com/data-grid-cell {} (let [entry-id (line->id (fc/field-value r))
(fc/with-field :external-id row-errors (seq (fc/field-errors))]
(com/validated-field ;; A ledger entry spans several rows. Each row knows its own
{:errors (fc/field-errors)} ;; entry id and drops itself when any of its siblings' remove
(com/text-input {:value (fc/field-value) ;; buttons announces that id. Removing the row removes its
:name (fc/field-name)})))) ;; inputs, so the entry is gone from the next import post.
(com/data-grid-cell {} ;; Clean rows are only hidden (x-show), never removed, so
(fc/with-field :client-code ;; "Only show errors" never changes what gets posted.
(com/validated-field (com/data-grid-row (cond-> {:data-entry-id entry-id
{:errors (fc/field-errors)} :x-data (hx/json {"entryId" entry-id})
(com/text-input {:value (fc/field-value) "@remove-import-entry.window" "if ($event.detail.entryId === entryId) $el.remove()"}
:name (fc/field-name) (not row-errors) (assoc :x-show "!errorsOnly"))
:class "w-24"})))) (com/data-grid-cell {}
(com/data-grid-cell {} (fc/with-field :external-id
(fc/with-field :source (com/validated-field
(com/validated-field {:errors (fc/field-errors)}
{:errors (fc/field-errors)} (com/text-input {:value (fc/field-value)
(com/text-input :name (fc/field-name)}))))
{:value (fc/field-value) (com/data-grid-cell {}
:name (fc/field-name)})))) (fc/with-field :client-code
(com/data-grid-cell {} (fc/with-field :vendor-name (com/validated-field
(com/validated-field {:errors (fc/field-errors)}
{:errors (fc/field-errors)} (com/text-input {:value (fc/field-value)
(com/text-input {:value (fc/field-value) :name (fc/field-name)
:name (fc/field-name)})))) :class "w-24"}))))
(com/data-grid-cell {} (fc/with-field :date (com/data-grid-cell {}
(com/validated-field (fc/with-field :source
{:errors (fc/field-errors)} (com/validated-field
(com/text-input {:value (some-> (fc/field-value) (atime/unparse-local {:errors (fc/field-errors)}
atime/normal-date)) (com/text-input
:name (fc/field-name) {:value (fc/field-value)
:class "w-24"})))) :name (fc/field-name)}))))
(com/data-grid-cell {} (com/data-grid-cell {} (fc/with-field :vendor-name
(fc/with-field :account-code (com/validated-field
(com/validated-field {:errors (fc/field-errors)} {:errors (fc/field-errors)}
(com/text-input {:value (fc/field-value) (com/text-input {:value (fc/field-value)
:name (fc/field-name) :name (fc/field-name)}))))
:class "w-16"})))) (com/data-grid-cell {} (fc/with-field :date
(com/data-grid-cell {} (fc/with-field :location (com/validated-field
(com/validated-field {:errors (fc/field-errors)}
{:errors (fc/field-errors)} (com/text-input {:value (some-> (fc/field-value) (atime/unparse-local
(com/text-input {:value (fc/field-value) atime/normal-date))
:name (fc/field-name) :name (fc/field-name)
:size 2})))) :class "w-24"}))))
(com/data-grid-cell {} (fc/with-field :debit (com/data-grid-cell {}
(com/validated-field {:errors (fc/field-errors)} (fc/with-field :account-code
(com/money-input {:value (fc/field-value) (com/validated-field {:errors (fc/field-errors)}
:name (fc/field-name) (com/text-input {:value (fc/field-value)
:class "w-24"})))) :name (fc/field-name)
(com/data-grid-cell {} (fc/with-field :credit :class "w-16"}))))
(com/validated-field {:errors (fc/field-errors)} (com/data-grid-cell {} (fc/with-field :location
(com/money-input {:value (fc/field-value) (com/validated-field
:name (fc/field-name) {:errors (fc/field-errors)}
:class "w-24"})))) (com/text-input {:value (fc/field-value)
(com/data-grid-cell {:class "align-top"} :name (fc/field-name)
[:div.p-2 :size 2}))))
(let [errors (seq (fc/field-errors))] (com/data-grid-cell {} (fc/with-field :debit
(cond errors (com/validated-field {:errors (fc/field-errors)}
[:div (com/money-input {:value (fc/field-value)
{"x-tooltip" "{content: ()=>$refs.tt.innerHTML , allowHTML: true}"} :name (fc/field-name)
[:div.w-8.h-8.rounded-full.p-2.flex.items-start {:class :class "w-24"}))))
(if (seq (filter (com/data-grid-cell {} (fc/with-field :credit
(fn [[_ status]] (com/validated-field {:errors (fc/field-errors)}
(com/money-input {:value (fc/field-value)
:name (fc/field-name)
:class "w-24"}))))
(com/data-grid-cell {:class "align-top"}
[:div.p-2.flex.items-start.gap-2
(let [errors (seq (fc/field-errors))]
(cond errors
[:div
{"x-tooltip" "{content: ()=>$refs.tt.innerHTML , allowHTML: true}"}
[:div.w-8.h-8.rounded-full.p-2.flex.items-start {:class
(if (seq (filter
(fn [[_ status]]
(= :error status)) (= :error status))
errors)) errors))
"bg-red-50 text-red-300" "bg-red-50 text-red-300"
"bg-yellow-100 text-yellow-600")} "bg-yellow-100 text-yellow-600")}
svg/alert] svg/alert]
[:template {:x-ref "tt"} [:template {:x-ref "tt"}
[:ul [:ul
(for [[m] errors] (for [[m] errors]
[:li m])]]] [:li m])]]]
:else :else
nil))]))))} nil))
(com/icon-button {:type "button"
:color :danger-light
:title (str "Remove ledger entry " entry-id)
:aria-label (str "Remove ledger entry " entry-id)
:data-remove-entry-id entry-id
"@click.prevent.stop" "$dispatch('remove-import-entry', {entryId: entryId})"}
svg/trash)])))))}
[:div.flex.m-4.flex-row-reverse [:div.flex.m-4.flex-row-reverse
(com/button {:color :primary} "Import")])]]])))]) (com/button {:color :primary} "Import")])]]])))])
@@ -408,9 +430,6 @@
(html-response (html-response
(external-import-form* (assoc request :just-parsed? true)))) (external-import-form* (assoc request :just-parsed? true))))
(defn line->id [{:keys [source external-id client-code]}]
(str client-code "-" source "-" external-id))
(defn add-errors [entry all-vendors all-accounts client-locked-lookup all-client-bank-accounts all-client-locations] (defn add-errors [entry all-vendors all-accounts client-locked-lookup all-client-bank-accounts all-client-locations]
(let [vendor (all-vendors (:vendor-name entry)) (let [vendor (all-vendors (:vendor-name entry))
locked-until (client-locked-lookup (:client-code entry)) locked-until (client-locked-lookup (:client-code entry))
@@ -507,12 +526,23 @@
ea))) ea)))
line-items))))) line-items)))))
(defn blank-amount?
"A row with neither a debit nor a credit carries no amount at all, so it is
skipped entirely on import rather than flagged."
[{:keys [debit credit]}]
(letfn [(blank? [v]
(or (nil? v)
(and (string? v) (str/blank? v))))]
(and (blank? debit) (blank? credit))))
(defn table->entries [table all-vendors all-accounts client-locked-lookup all-client-bank-accounts all-client-locations] (defn table->entries [table all-vendors all-accounts client-locked-lookup all-client-bank-accounts all-client-locations]
(let [lines-with-indexes (for [[i l] (map vector (range) table)] (let [lines-with-indexes (for [[i l] (map vector (range) table)]
(assoc l :index i))] (assoc l :index i))]
(into [] (into []
(for [[_ lines] (group-by line->id lines-with-indexes) (for [[_ grouped-lines] (group-by line->id lines-with-indexes)
:let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]] :let [lines (remove blank-amount? grouped-lines)
{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]
:when (seq lines)]
(add-errors {:source source (add-errors {:source source
:indices (map :index lines) :indices (map :index lines)
:external-id (line->id line) :external-id (line->id line)

View File

@@ -16,12 +16,12 @@
[auto-ap.ssr.components :as com] [auto-ap.ssr.components :as com]
[auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx]
[auto-ap.ssr.ledger.export-modal :refer [export-modal]]
[auto-ap.ssr.ledger.report-table :as rtable] [auto-ap.ssr.ledger.report-table :as rtable]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers clj-date-schema :refer [apply-middleware-to-all-handlers clj-date-schema
html-response modal-response wrap-form-4xx-2 html-response wrap-form-4xx-2
wrap-schema-enforce]] wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
@@ -89,6 +89,7 @@
:account-type (:account_type account) :account-type (:account_type account)
:numeric-code (:numeric_code account) :numeric-code (:numeric_code account)
:name (:name account) :name (:name account)
:bank-account-name? (:bank_account_name? account)
:period (coerce/to-date d)})) :period (coerce/to-date d)}))
args (assoc (:query-params request) args (assoc (:query-params request)
:periods (map coerce/to-date (filter identity date))) :periods (map coerce/to-date (filter identity date)))
@@ -235,30 +236,12 @@
:report/creator (:user (:identity request)) :report/creator (:user (:identity request))
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url})) :report/url url
:report/clients client}))
;; TODO PRINT WARNING ;; TODO PRINT WARNING
(defn export [request] (defn export [request]
(modal-response (export-modal request (print-balance-sheet request)))
(com/modal {}
(com/modal-card
{}
"Ready!"
(com/modal-body {}
(let [bs (print-balance-sheet request)]
[:div.flex.flex-col.mt-4.space-y-4.items-center
[:a {:href (:report/url bs)}
[:div.w-24.h-24.bg-green-50.rounded-full.p-4.text-green-300 {:class " hover:scale-110 transition duration-100"}
svg/download]]
[:span.text-gray-800
"Click "
(com/link {:href (:report/url bs)} "here")
" to download"]]))
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
(assoc "hx-reswap" "beforeend"))))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers

View File

@@ -16,13 +16,13 @@
[auto-ap.ssr.components :as com] [auto-ap.ssr.components :as com]
[auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx]
[auto-ap.ssr.ledger.export-modal :refer [export-modal]]
[auto-ap.ssr.ledger.report-table :refer [cell-count concat-tables table]] [auto-ap.ssr.ledger.report-table :refer [cell-count concat-tables table]]
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers clj-date-schema html-response :refer [apply-middleware-to-all-handlers clj-date-schema html-response
modal-response wrap-form-4xx-2 wrap-schema-enforce]] wrap-form-4xx-2 wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-pdf.core :as pdf] [clj-pdf.core :as pdf]
@@ -88,6 +88,7 @@
:account-type (:account_type account) :account-type (:account_type account)
:numeric-code (:numeric_code account) :numeric-code (:numeric_code account)
:name (:name account) :name (:name account)
:bank-account-name? (:bank_account_name? account)
:period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}})) :period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
args (assoc (:form-params request) args (assoc (:form-params request)
:periods (map (fn [d] {:start (coerce/to-date (:start d)) :end (coerce/to-date (:end d))}) periods)) :periods (map (fn [d] {:start (coerce/to-date (:start d)) :end (coerce/to-date (:end d))}) periods))
@@ -226,31 +227,12 @@
:report/creator (:user (:identity request)) :report/creator (:user (:identity request))
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url})) :report/url url
:report/clients client}))
;; TODO PRINT WARNING ;; TODO PRINT WARNING
(defn export [request] (defn export [request]
(modal-response (export-modal request (print-cash-flows request)))
(com/modal {}
(com/modal-card
{}
"Ready!"
(com/modal-body {}
(let [bs (print-cash-flows request)]
[:div.flex.flex-col.mt-4.space-y-4.items-center
[:a {:href (:report/url bs)}
[:div.w-24.h-24.bg-green-50.rounded-full.p-4.text-green-300 {:class " hover:scale-110 transition duration-100"}
svg/download]]
[:span.text-gray-800
"Click "
(com/link {:href (:report/url bs)} "here")
" to download"]]))
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
(assoc "hx-reswap" "beforeend"))))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers

View File

@@ -109,6 +109,33 @@
:placeholder "e.g., ABC-456" :placeholder "e.g., ABC-456"
:size :small})) :size :small}))
(when (:external? (:route-params request))
(com/field {:label "Source"}
(com/text-input {:name "source"
:id "source"
:class "hot-filter"
:value (:source (:query-params request))
:placeholder "e.g., invoice"
:size :small})))
(when (:external? (:route-params request))
(com/field {:label "External Id"}
(com/text-input {:name "external-id-like"
:id "external-id-like"
:class "hot-filter"
:value (:external-id-like (:query-params request))
:placeholder "e.g., ABC-123"
:size :small})))
(when (:external? (:route-params request))
(com/field {:label "Location"}
(com/text-input {:name "location"
:id "location"
:class "hot-filter"
:value (:location (:query-params request))
:placeholder "SC"
:size :small})))
(com/field {:label "Account Code"} (com/field {:label "Account Code"}
[:div.flex.space-x-4.items-baseline [:div.flex.space-x-4.items-baseline
(com/int-input {:name "numeric-code-gte" (com/int-input {:name "numeric-code-gte"
@@ -176,12 +203,76 @@
result)) result))
results)) results))
(defn- entries-off-reports
"Journal entries with a line posted to one of `clients`' bank accounts flagged
`:bank-account/include-in-reports` false, or nil when there are none.
The GraphQL ledger page has always dropped these (see
auto-ap.datomic.ledger/graphql-results), so the register has to agree --
otherwise the same entry is visible on one ledger page and missing from the
other. Resolved up front off VAET, which is ~150 entries db-wide and costs
~2ms; excluding them inside the query with a not-join instead measured ~10x
the cost of the whole scan on a wide date range."
[db clients]
(when-let [off-reports (seq (dc/q '[:find [?ba ...]
:in $ [?client ...]
:where
[?client :client/bank-accounts ?ba]
[?ba :bank-account/include-in-reports false]]
db clients))]
(set (dc/q '[:find [?e ...]
:in $ [?ba ...]
:where
[?li :journal-entry-line/account ?ba]
[?e :journal-entry/line-items ?li]]
db off-reports))))
(defn- apply-include-in-reports [db clients results]
(if-let [hidden (entries-off-reports db clients)]
(remove (comp hidden last) results)
results))
;; TODO ;; TODO
;; 1. Sorting in investigate dialog ;; 1. Sorting in investigate dialog
;; 2. actual date range filtering in investigate dialog ;; 2. actual date range filtering in investigate dialog
;; 3. CSVs ;; 3. CSVs
;; 4. better date range / advanced mode for dialog ;; 4. better date range / advanced mode for dialog
(defn- accounts-sharing-code
"The searched account, plus any of `clients`' bank accounts on the same numeric
code. :journal-entry-line/account points at either entity, so a search for the
financial account has to match lines posted to the bank account too."
[db clients account-id]
(into [account-id]
(when-let [code (:account/numeric-code (dc/entity db account-id))]
(dc/q '[:find [?ba ...]
:in $ [?client ...] ?code
:where
[?client :client/bank-accounts ?ba]
[?ba :bank-account/numeric-code ?code]]
db clients code))))
(defn- account-filter-query
"Ledger clause for the Account search. Stays a scalar binding when nothing
shares the account's code, so the common case costs what it always did."
[db clients account-id]
(let [ids (accounts-sharing-code db clients account-id)]
(if (second ids)
{:query {:in ['[?a3 ...]]
:where ['[?li :journal-entry-line/account ?a3]]}
:args [ids]}
{:query {:in ['?a3]
:where ['[?li :journal-entry-line/account ?a3]]}
:args [account-id]})))
(defn fetch-ids [db {:keys [query-params route-params] :as request}] (defn fetch-ids [db {:keys [query-params route-params] :as request}]
(let [valid-clients (extract-client-ids (:clients request) (let [valid-clients (extract-client-ids (:clients request)
(:client-id request) (:client-id request)
@@ -261,9 +352,7 @@
'[(<= ?c ?to-numeric-code)]]} '[(<= ?c ?to-numeric-code)]]}
:args [(map (juxt :from :to) (:numeric-code args))]}) :args [(map (juxt :from :to) (:numeric-code args))]})
(seq (:account args)) (seq (:account args))
(merge-query {:query {:in ['?a3] (merge-query (account-filter-query db valid-clients (:db/id (:account args))))
:where ['[?li :journal-entry-line/account ?a3]]}
:args [(:db/id (:account args))]})
(:amount-gte args) (:amount-gte args)
(merge-query {:query {:in ['?amount-gte] (merge-query {:query {:in ['?amount-gte]
@@ -319,6 +408,7 @@
(merge-query {:query {:find ['?sort-default '?e]}})))] (merge-query {:query {:find ['?sort-default '?e]}})))]
(->> (observable-query query) (->> (observable-query query)
(apply-include-in-reports db valid-clients)
(apply-sort-4 (assoc query-params :default-asc? true)) (apply-sort-4 (assoc query-params :default-asc? true))
(apply-only-unbalanced query-params) (apply-only-unbalanced query-params)
(apply-pagination query-params)))) (apply-pagination query-params))))
@@ -455,6 +545,9 @@
[:account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :account/name]}]]] [:account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :account/name]}]]]
[:check-number {:optional true} [:maybe [:string {:decode/string strip}]]] [:check-number {:optional true} [:maybe [:string {:decode/string strip}]]]
[:invoice-number {:optional true} [:maybe [:string {:decode/string strip}]]] [:invoice-number {:optional true} [:maybe [:string {:decode/string strip}]]]
[:source {:optional true} [:maybe [:string {:decode/string strip}]]]
[:external-id-like {:optional true} [:maybe [:string {:decode/string strip}]]]
[:location {:optional true} [:maybe [:string {:decode/string strip}]]]
[:status {:optional true} [:maybe (ref->enum-schema "invoice-status")]] [:status {:optional true} [:maybe (ref->enum-schema "invoice-status")]]
[:exact-match-id {:optional true} [:maybe entity-id]] [:exact-match-id {:optional true} [:maybe entity-id]]
[:all-selected {:optional true :default nil} [:maybe :boolean]] [:all-selected {:optional true :default nil} [:maybe :boolean]]
@@ -528,7 +621,9 @@
:title (fn [r] :title (fn [r]
(str (str
(some-> r :route-params :status name str/capitalize (str " ")) (some-> r :route-params :status name str/capitalize (str " "))
"Register")) (if (:external? (:route-params r))
"External Register"
"Register")))
:entity-name "register" :entity-name "register"
:route ::route/table :route ::route/table
:csv-route ::route/csv :csv-route ::route/csv
@@ -595,6 +690,12 @@
:render (fn [{:journal-entry/keys [amount]}] :render (fn [{:journal-entry/keys [amount]}]
(some->> amount (some->> amount
(format "$%,.2f")))} (format "$%,.2f")))}
{:key "account-number"
:name "Account Number"
:class "text-right"
:render-csv #(or (-> % :journal-entry-line/account :account/numeric-code)
(-> % :journal-entry-line/account :bank-account/numeric-code))
:render-for #{:csv}}
{:key "account" {:key "account"
:name "Account" :name "Account"
:sort-key "account" :sort-key "account"
@@ -629,14 +730,15 @@
:color :primary :color :primary
:content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))}) :content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))})
(-> i :journal-entry/original-entity :invoice/source-url) (-> i :journal-entry/original-entity :invoice/source-url)
{:link (-> i :journal-entry/original-entity :invoice/source-url) (conj
:color :secondary {:link (-> i :journal-entry/original-entity :invoice/source-url)
:content (str "File")} :color :secondary
:content (str "File")})
(-> i :journal-entry/original-entity :transaction/description-original) (-> i :journal-entry/original-entity :transaction/description-original)
(conj (conj
{:link (hu/url (bidi/path-for ssr-routes/only-routes {:link (hu/url (bidi/path-for ssr-routes/only-routes
::transaction-routes/all-page) ::transaction-routes/page)
{:exact-match-id (:db/id (:journal-entry/original-entity i))}) {:exact-match-id (:db/id (:journal-entry/original-entity i))})
:color :primary :color :primary
:content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))}) :content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))})

View File

@@ -0,0 +1,148 @@
(ns auto-ap.ssr.ledger.export-modal
"Shared \"your report is ready\" modal for the SSR ledger reports.
Offers the generated PDF for download and, for admins running a report for a
single client, a `mailto:` hand-off pre-filled with that client's email
contacts. Nothing is sent by the server - the link opens the user's own mail
client so they can review before sending."
(:require
[auto-ap.datomic :refer [conn pull-many]]
[auto-ap.graphql.utils :refer [is-admin?]]
[auto-ap.routes.transactions :as transaction-routes]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils :refer [modal-response]]
[bidi.bidi :as bidi]
[clojure.string :as str]
[config.core :refer [env]]
[datomic.api :as dc])
(:import
[java.net URLEncoder]))
(def recipient-separator
"RFC 6068 separator for multiple mailto recipients. Outlook, Gmail and Apple
Mail all accept a comma; the legacy SPA used a semicolon, which only Outlook
understood."
",")
(defn url-encode
"Percent-encode a mailto header value. URLEncoder does form encoding, so
spaces come back as `+`; mail clients want %20."
[s]
(-> (URLEncoder/encode (str s) "UTF-8")
(str/replace "+" "%20")))
(defn app-url [route]
(str (:base-url env) (bidi/path-for ssr-routes/only-routes route)))
(defn email-body [report-url]
(str
"Hello,
Click here (" report-url ") to download your financial reports. We have not finished reviewing and reconciling these numbers with you. Please review and let us know if anything seems missing or in need of correction.
Click here (" (:base-url env) ") to login to the Financials app to review the details here.
Click here (https://share.vidyard.com/watch/MHTo5PyXPxXUpVH93RWFM9?) for a video on how to run a P&L on your own.
To see a history of past financial reports, click here: " (app-url :company-reports) "
NOTE: Please review the transactions we may have question for you here: " (app-url ::transaction-routes/requires-feedback-page) ". You can either edit the transaction to what expense account it should be or email back what it should be."))
(defn email-contacts
"Email contacts configured on `clients` (a seq of entity maps with :db/id)."
[clients]
(->> (pull-many (dc/db conn)
[{:client/emails [:email-contact/email :email-contact/description]}]
(map :db/id clients))
(mapcat :client/emails)
(filter :email-contact/email)))
(defn recipients
"The contacts to offer a mailto hand-off to, or nil when we should not offer
one: only admins may email a report out, and only for a single client - a
multi-client report would expose one client's numbers to another's contacts."
[request clients]
(when (and (is-admin? (:identity request))
(= 1 (count clients)))
(seq (email-contacts clients))))
(defn mailto-href [contacts {:report/keys [name url]}]
(str "mailto:" (str/join recipient-separator (map :email-contact/email contacts))
"?subject=" (url-encode (str name " is ready"))
"&body=" (url-encode (email-body url))))
(def eyebrow-class
"Section label for the two hand-offs. The modal is a transmittal slip: each
section is one destination the report can go to."
"text-[11px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500")
(defn contact-row
"One recipient, as a role/address pair. The contacts used to be joined into
the prose of a sentence, which ran on past two of them - as a distribution
list they stay scannable however many the client has. Returns the grid cells;
the columns are sized by the parent so every address lines up. `roles?` is
false when no contact has a description, so the empty role column is dropped
rather than indenting every address past nothing."
[roles? {:email-contact/keys [email description]}]
(list
(when roles?
[:dt {:class "max-w-[10rem] truncate text-[11px] font-medium uppercase tracking-wide text-gray-400 dark:text-gray-500"}
description])
[:dd {:class "min-w-0 truncate text-xs text-gray-700 dark:text-gray-200" :title email}
email]))
(defn modal-header []
[:div {:class "flex items-start justify-between gap-4 border-b border-gray-200 px-6 py-4 dark:border-gray-600"}
[:div {:class "flex items-center gap-3"}
[:span {:class "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"}
[:div.h-3.w-3 svg/checkmark]]
[:h3 {:class "text-base font-semibold text-gray-900 dark:text-white"} "Your report is ready"]]
[:button {:type "button"
"@click" "$dispatch('modalclose')"
:aria-label "Close"
:class "shrink-0 rounded-lg p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 focus:ring-2 focus:ring-green-400 dark:hover:bg-gray-600 dark:hover:text-white"}
[:div.h-4.w-4 svg/x]]])
(defn download-section
"`labelled?` is false when the report has nowhere else to go - with no second
destination to contrast it against, \"Your copy\" labels nothing."
[labelled? {:report/keys [name url]}]
[:section {:class "px-6 py-5"}
(when labelled?
[:p {:class eyebrow-class} "Your copy"])
[:div {:class (str (when labelled? "mt-3 ") "flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-4")}
[:div {:class "flex min-w-0 grow items-center gap-3"}
[:div {:class "flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500"}
[:div.h-5.w-5 svg/accounting-invoice-mail]]
[:p {:class "min-w-0 break-words text-sm font-medium text-gray-900 dark:text-gray-100"} name]]
(com/a-button {:href url :color :primary :download name :indicator? false
:class "mr-0 w-full shrink-0 sm:w-auto"}
[:div.h-4.w-4 svg/download]
"Download PDF")]])
(defn email-section [contacts report]
(let [roles? (boolean (some (comp not str/blank? :email-contact/description) contacts))]
[:section {:class "border-t border-gray-200 px-6 py-5 dark:border-gray-600"}
[:p {:class eyebrow-class} "Send to client"]
[:dl {:class (str "mt-3 grid max-h-40 items-baseline gap-x-4 gap-y-1.5 overflow-y-auto "
(if roles? "grid-cols-[max-content_minmax(0,1fr)]" "grid-cols-1"))}
(map (partial contact-row roles?) contacts)]
[:div {:class "mt-5 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"}
[:p {:class "text-xs text-gray-500 dark:text-gray-400"}
"Nothing sends until you send it."]
(com/a-button {:href (mailto-href contacts report) :indicator? false
:class "mr-0 w-full shrink-0 sm:w-auto"}
[:div.h-4.w-4 svg/envelope]
"Open email draft")]]))
(defn export-modal
"Modal response for a freshly printed `report` - the map returned by the
report namespaces' print-* functions."
[request {:report/keys [clients] :as report}]
(let [contacts (recipients request clients)]
(modal-response
(com/modal {}
(com/modal-card-advanced
{:class "m-4 w-full md:m-0 md:w-[560px]"}
(modal-header)
(download-section (boolean contacts) report)
(when contacts
(email-section contacts report)))))))

View File

@@ -16,13 +16,13 @@
[auto-ap.ssr.components :as com] [auto-ap.ssr.components :as com]
[auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx]
[auto-ap.ssr.ledger.export-modal :refer [export-modal]]
[auto-ap.ssr.ledger.report-table :refer [cell-count concat-tables table]] [auto-ap.ssr.ledger.report-table :refer [cell-count concat-tables table]]
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers clj-date-schema html-response :refer [apply-middleware-to-all-handlers clj-date-schema html-response
modal-response wrap-form-4xx-2 wrap-schema-enforce]] wrap-form-4xx-2 wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-pdf.core :as pdf] [clj-pdf.core :as pdf]
@@ -101,6 +101,7 @@
:account-type (:account_type account) :account-type (:account_type account)
:numeric-code (:numeric_code account) :numeric-code (:numeric_code account)
:name (:name account) :name (:name account)
:bank-account-name? (:bank_account_name? account)
:sample sample :sample sample
:period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}})) :period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
@@ -282,31 +283,12 @@
:report/creator (:user (:identity request)) :report/creator (:user (:identity request))
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url})) :report/url url
:report/clients client}))
;; TODO PRINT WARNING ;; TODO PRINT WARNING
(defn export [request] (defn export [request]
(modal-response (export-modal request (print-profit-and-loss request)))
(com/modal {}
(com/modal-card
{}
"Ready!"
(com/modal-body {}
(let [bs (print-profit-and-loss request)]
[:div.flex.flex-col.mt-4.space-y-4.items-center
[:a {:href (:report/url bs)}
[:div.w-24.h-24.bg-green-50.rounded-full.p-4.text-green-300 {:class " hover:scale-110 transition duration-100"}
svg/download]]
[:span.text-gray-800
"Click "
(com/link {:href (:report/url bs)} "here")
" to download"]]))
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
(assoc "hx-reswap" "beforeend"))))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers

View File

@@ -423,7 +423,7 @@
{:exact-match-id (:db/id invoice)}) {:exact-match-id (:db/id invoice)})
:content (str "Inv. " (:invoice/invoice-number invoice))}))) :content (str "Inv. " (:invoice/invoice-number invoice))})))
(some-> p :transaction/_payment ((fn [t] (some-> p :transaction/_payment ((fn [t]
[{:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page) [{:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/page)
{:exact-match-id (:db/id (first t))}) {:exact-match-id (:db/id (first t))})
:color :secondary :color :secondary
:content "Transaction"}]))))))}]})) :content "Transaction"}]))))))}]}))

View File

@@ -165,7 +165,7 @@
svg/external-link)) svg/external-link))
(when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)] (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)]
(com/a-button {:href (str (bidi/path-for ssr-routes/only-routes (com/a-button {:href (str (bidi/path-for ssr-routes/only-routes
::transaction-routes/all-page) ::transaction-routes/page)
"?exact-match-id=" "?exact-match-id="
transaction-id)} "Transaction"))]) transaction-id)} "Transaction"))])
:headers [{:key "client" :headers [{:key "client"

View File

@@ -2,7 +2,7 @@
(:require (:require
[auto-ap.datomic [auto-ap.datomic
:refer [apply-pagination apply-sort-3 conn merge-query pull-many :refer [apply-pagination apply-sort-3 conn merge-query pull-many
query2]] pull-many-by-id query2]]
[auto-ap.datomic.accounts :as d-accounts] [auto-ap.datomic.accounts :as d-accounts]
[auto-ap.graphql.utils :refer [extract-client-ids]] [auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]] [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
@@ -128,6 +128,38 @@
(map #(:ledger-mapped/amount % 0.0)) (map #(:ledger-mapped/amount % 0.0))
(reduce + 0.0))) (reduce + 0.0)))
(def csv-account-read
[:db/id
:account/name
:account/code
{:account/client-overrides [:account-client-override/name
{:account-client-override/client [:db/id]}]}])
(defn- csv-amount
"Renders an item's amount when it falls on `side`, fixed to cents so the CSV
never carries floating point noise."
[side item]
(when (= side (:ledger-mapped/ledger-side item))
(format "%.2f" (double (:ledger-mapped/amount item 0.0)))))
(defn summaries->csv-rows
"Flattens each summary into one row per item, so every category is its own
auditable CSV line. Accounts are resolved in a single batch pull and
clientized against the summary's own client."
[summaries]
(let [account-ids (into #{}
(comp (mapcat :sales-summary/items)
(keep (comp :db/id :ledger-mapped/account)))
summaries)
id->account (if (seq account-ids)
(pull-many-by-id (dc/db conn) csv-account-read account-ids)
{})]
(for [ss summaries
item (sort-items (:sales-summary/items ss))]
(assoc (merge item ss)
::account (some-> item :ledger-mapped/account :db/id id->account
(d-accounts/clientize (-> ss :sales-summary/client :db/id)))))))
(defn truncate [s max-len] (defn truncate [s max-len]
(if (> (count s) max-len) (if (> (count s) max-len)
(str (subs s 0 (- max-len 3)) "...") (str (subs s 0 (- max-len 3)) "...")
@@ -215,7 +247,15 @@
:title "Sales Summaries" :title "Sales Summaries"
:entity-name "Daily Summary" :entity-name "Daily Summary"
:route ::route/table :route ::route/table
:headers [{:key "client" :csv-route ::route/csv
:page->csv-entities (fn [[summaries]]
(summaries->csv-rows summaries))
:headers [{:key "id"
:name "Id"
:render-csv :db/id
:render-for #{:csv}}
{:key "client"
:name "Client" :name "Client"
:sort-key "client" :sort-key "client"
:hide? (fn [args] :hide? (fn [args]
@@ -231,6 +271,7 @@
:name "Debits" :name "Debits"
:sort-key "debits" :sort-key "debits"
:class "w-72 align-top" :class "w-72 align-top"
:render-for #{:html}
:render (fn [ss] :render (fn [ss]
(let [items (:sales-summary/items ss) (let [items (:sales-summary/items ss)
debit-items (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)) (sort-items items)) debit-items (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)) (sort-items items))
@@ -257,6 +298,7 @@
:name "Credits" :name "Credits"
:sort-key "credits" :sort-key "credits"
:class "w-72 align-top" :class "w-72 align-top"
:render-for #{:html}
:render (fn [ss] :render (fn [ss]
(let [items (:sales-summary/items ss) (let [items (:sales-summary/items ss)
credit-items (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)) (sort-items items)) credit-items (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)) (sort-items items))
@@ -283,6 +325,7 @@
:name "Status" :name "Status"
:sort-key "balance" :sort-key "balance"
:class "w-28 align-top" :class "w-28 align-top"
:render-for #{:html}
:render (fn [ss] :render (fn [ss]
(let [items (:sales-summary/items ss) (let [items (:sales-summary/items ss)
total-debits (total-debits items) total-debits (total-debits items)
@@ -304,10 +347,41 @@
[:span.text-xs.uppercase.tracking-wider.text-red-600.font-medium.mt-0.5 [:span.text-xs.uppercase.tracking-wider.text-red-600.font-medium.mt-0.5
(if (> total-debits total-credits) "Debit over" "Credit over")]])]))} (if (> total-debits total-credits) "Debit over" "Credit over")]])]))}
{:key "category"
:name "Category"
:render-csv :sales-summary-item/category
:render-for #{:csv}}
{:key "account-code"
:name "Account Code"
:render-csv #(-> % ::account :account/code)
:render-for #{:csv}}
{:key "account"
:name "Account"
:render-csv #(-> % ::account :account/name)
:render-for #{:csv}}
{:key "debit"
:name "Debit"
:render-csv (partial csv-amount :ledger-side/debit)
:render-for #{:csv}}
{:key "credit"
:name "Credit"
:render-csv (partial csv-amount :ledger-side/credit)
:render-for #{:csv}}
{:key "manual"
:name "Manual"
:render-csv #(boolean (:sales-summary-item/manual? %))
:render-for #{:csv}}
{:key "links" {:key "links"
:name "Links" :name "Links"
:show-starting "lg" :show-starting "lg"
:class "w-8" :class "w-8"
:render-for #{:html}
:render (fn [ss] :render (fn [ss]
(let [ledger-entry (:journal-entry/original-entity ss)] (let [ledger-entry (:journal-entry/original-entity ss)]
(when (seq ledger-entry) (when (seq ledger-entry)
@@ -754,6 +828,7 @@
(->> (->>
{::route/page (helper/page-route grid-page) {::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page) ::route/table (helper/table-route grid-page)
::route/csv (helper/csv-route grid-page)
::route/edit-wizard (-> mm/open-wizard-handler ::route/edit-wizard (-> mm/open-wizard-handler
(mm/wrap-wizard edit-wizard) (mm/wrap-wizard edit-wizard)
(mm/wrap-init-multi-form-state initial-edit-wizard-state) (mm/wrap-init-multi-form-state initial-edit-wizard-state)

View File

@@ -114,6 +114,14 @@
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "2", :stroke "currentColor", :aria-hidden "true"} [:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "2", :stroke "currentColor", :aria-hidden "true"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"}]]) [:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"}]])
(def checkmark
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "3", :stroke "currentColor", :aria-hidden "true"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M4.5 12.75l6 6 9-13.5"}]])
(def envelope
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "1.5", :stroke "currentColor", :aria-hidden "true"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"}]])
(def vendors (def vendors
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"} [:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:defs] [:defs]

View File

@@ -132,7 +132,13 @@
valid-clients]} valid-clients]}
(cond-> {:query {:find [] (cond-> {:query {:find []
:in ['$ '[?clients ?start ?end]] :in ['$ '[?clients ?start ?end]]
:where '[[(iol-ion.query/scan-transactions $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]]} ;; Suppressed transactions are never listed -- the GraphQL page has
;; always dropped them (auto-ap.datomic.transactions/graphql-results),
;; as do the ledger queries. The three status routes (approved,
;; unapproved, requires-feedback) constrain the status themselves, and
;; there is no suppressed route, so this never hides a page's own rows.
:where '[[(iol-ion.query/scan-transactions $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
(not [?e :transaction/approval-status :transaction-approval-status/suppressed])]}
:args [db :args [db
[valid-clients [valid-clients
(some-> (:start-date query-params) coerce/to-date) (some-> (:start-date query-params) coerce/to-date)
@@ -248,6 +254,19 @@
[?v :vendor/name ?sort-vendor]) [?v :vendor/name ?sort-vendor])
(and [(missing? $ ?e :transaction/vendor)] (and [(missing? $ ?e :transaction/vendor)]
[(ground "") ?sort-vendor]))] [(ground "") ?sort-vendor]))]
"bank-account" '[(or-join [?e ?sort-bank-account]
(and [?e :transaction/bank-account ?sort-ba]
[?sort-ba :bank-account/name ?sort-bank-account])
(and [?e :transaction/bank-account ?sort-ba]
[(missing? $ ?sort-ba :bank-account/name)]
[?sort-ba :bank-account/numeric-code ?sort-ba-code]
[(str ?sort-ba-code) ?sort-bank-account])
(and [?e :transaction/bank-account ?sort-ba]
[(missing? $ ?sort-ba :bank-account/name)]
[(missing? $ ?sort-ba :bank-account/numeric-code)]
[(ground "") ?sort-bank-account])
(and [(missing? $ ?e :transaction/bank-account)]
[(ground "") ?sort-bank-account]))]
"date" ['[?e :transaction/date ?sort-date]] "date" ['[?e :transaction/date ?sort-date]]
"amount" ['[?e :transaction/amount ?sort-amount]] "amount" ['[?e :transaction/amount ?sort-amount]]
"description" ['[?e :transaction/description-original ?sort-description]]} "description" ['[?e :transaction/description-original ?sort-description]]}
@@ -538,6 +557,7 @@
:render-csv (fn [x] (-> x :transaction/client :client/name))} :render-csv (fn [x] (-> x :transaction/client :client/name))}
{:key "bank-account" {:key "bank-account"
:name "Bank Account" :name "Bank Account"
:sort-key "bank-account"
:show-starting "lg" :show-starting "lg"
:render (fn [x] :render (fn [x]
(let [ba (:transaction/bank-account x)] (let [ba (:transaction/bank-account x)]

View File

@@ -45,7 +45,7 @@
(def transaction-approval-status (def transaction-approval-status
{:transaction-approval-status/unapproved "Unapproved" {:transaction-approval-status/unapproved "Unapproved"
:transaction-approval-status/approved "Approved" :transaction-approval-status/approved "Approved"
:transaction-approval-status/suppressed "Client Review"}) :transaction-approval-status/requires-feedback "Client Review"})
(def row* (partial helper/row* grid-page)) (def row* (partial helper/row* grid-page))
@@ -988,8 +988,8 @@
":class" "{ '!bg-primary-200 text-primary-800': approvalStatus === 'unapproved' }" ":class" "{ '!bg-primary-200 text-primary-800': approvalStatus === 'unapproved' }"
:class "rounded-r-lg"} :class "rounded-r-lg"}
"Unapproved") "Unapproved")
(com/button-group-button {"@click" "approvalStatus = 'suppressed'" (com/button-group-button {"@click" "approvalStatus = 'requires-feedback'"
":class" "{ '!bg-primary-200 text-primary-800': approvalStatus === 'suppressed' }" ":class" "{ '!bg-primary-200 text-primary-800': approvalStatus === 'requires-feedback' }"
:class "rounded-r-lg"} :class "rounded-r-lg"}
"Client Review")]])))]]]]) "Client Review")]])))]]]])
:footer :footer
@@ -1049,7 +1049,7 @@
(when-not (dollars= (- (:transaction/amount transaction)) (when-not (dollars= (- (:transaction/amount transaction))
(:payment/amount payment)) (:payment/amount payment))
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"}))) (form-validation-error "Amounts don't match"))
(if (is-already-linked-to-this-payment? transaction payment-id) (if (is-already-linked-to-this-payment? transaction payment-id)
(save-memo-only request) (save-memo-only request)
(save-linked-transaction request payment)) (save-linked-transaction request payment))
@@ -1077,16 +1077,16 @@
(exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction)))
(when (:transaction/payment transaction) (when (:transaction/payment transaction)
(throw (ex-info "Transaction already linked" {:validation-error "Transaction already linked"}))) (form-validation-error "Transaction already linked"))
(when (or (> (count invoice-clients) 1) (when (or (> (count invoice-clients) 1)
(not= (-> transaction :transaction/client :db/id) (not= (-> transaction :transaction/client :db/id)
(first invoice-clients))) (first invoice-clients)))
(throw (ex-info "Clients don't match" {:validation-error "Invoice(s) and transaction client do not match."}))) (form-validation-error "Invoice(s) and transaction client do not match."))
(when-not (dollars= (- (:transaction/amount transaction)) (when-not (dollars= (- (:transaction/amount transaction))
invoice-amount) invoice-amount)
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"}))) (form-validation-error "Amounts don't match"))
(let [payment-tx (i-transactions/add-new-payment (let [payment-tx (i-transactions/add-new-payment
(dc/pull db [:transaction/amount :transaction/date :db/id] (:db/id transaction)) (dc/pull db [:transaction/amount :transaction/date :db/id] (:db/id transaction))
@@ -1122,16 +1122,16 @@
(when (or (> (count invoice-clients) 1) (when (or (> (count invoice-clients) 1)
(not= (-> transaction :transaction/client :db/id) (not= (-> transaction :transaction/client :db/id)
(first invoice-clients))) (first invoice-clients)))
(throw (ex-info "Clients don't match" {:validation-error "Invoice(s) and transaction client do not match." (form-validation-error "Invoice(s) and transaction client do not match."
:transaction-client (-> transaction :transaction/client :db/id) :transaction-client (-> transaction :transaction/client :db/id)
:invoice-clients invoice-clients}))) :invoice-clients invoice-clients))
(when-not (dollars= (- (:transaction/amount transaction)) (when-not (dollars= (- (:transaction/amount transaction))
invoice-amount) invoice-amount)
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"}))) (form-validation-error "Amounts don't match"))
(when (:transaction/payment transaction) (when (:transaction/payment transaction)
(throw (ex-info "Transaction already linked" {:validation-error "Transaction already linked"}))) (form-validation-error "Transaction already linked"))
(let [payment-tx (i-transactions/add-new-payment (let [payment-tx (i-transactions/add-new-payment
(dc/pull db [:transaction/amount :transaction/date :db/id] (:db/id transaction)) (dc/pull db [:transaction/amount :transaction/date :db/id] (:db/id transaction))
@@ -1178,12 +1178,10 @@
(let [description-pattern (some-> transaction-rule :transaction-rule/description iol-ion.query/->pattern)] (let [description-pattern (some-> transaction-rule :transaction-rule/description iol-ion.query/->pattern)]
(when (not (rm/rule-applies? transaction {:transaction-rule/description description-pattern})) (when (not (rm/rule-applies? transaction {:transaction-rule/description description-pattern}))
(throw (ex-info "Transaction rule does not apply" (form-validation-error "Transaction rule does not apply")))
{:validation-error "Transaction rule does not apply"}))))
(when (:transaction/payment transaction) (when (:transaction/payment transaction)
(throw (ex-info "Transaction already associated with a payment" (form-validation-error "Transaction already associated with a payment"))
{:validation-error "Transaction already associated with a payment"})))
(let [locations (-> transaction :transaction/client :client/locations) (let [locations (-> transaction :transaction/client :client/locations)
updated-tx (rm/apply-rule {:db/id (:db/id transaction) updated-tx (rm/apply-rule {:db/id (:db/id transaction)
@@ -1336,8 +1334,7 @@
(exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction))) (exception->4xx #(assert-not-locked (-> transaction :transaction/client :db/id) (:transaction/date transaction)))
(when (not= :payment-status/cleared (-> payment :payment/status)) (when (not= :payment-status/cleared (-> payment :payment/status))
(throw (ex-info "Payment can't be undone because it isn't cleared." (form-validation-error "Payment can't be undone because it isn't cleared."))
{:validation-error "Payment can't be undone because it isn't cleared."})))
(let [is-autopay-payment? (some->> (dc/q {:find ['?sp] (let [is-autopay-payment? (some->> (dc/q {:find ['?sp]
:in ['$ '?payment] :in ['$ '?payment]

View File

@@ -177,7 +177,7 @@
(:form-params request) (:form-errors request) (:form-params request) (:form-errors request)
(fc/with-field :table (fc/with-field :table
(when (seq (fc/field-value)) (when (seq (fc/field-value))
[:div.mt-4 {:x-data (hx/json {"showTable" true})} [:div.mt-4 {:x-data (hx/json {"showTable" true "errorsOnly" false})}
(when (:just-parsed? request) (when (:just-parsed? request)
(parsed-banner request)) (parsed-banner request))
[:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/external-import-import) [:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/external-import-import)
@@ -186,8 +186,9 @@
:autocomplete "off"} :autocomplete "off"}
[:div.flex.gap-4.items-center.my-2 [:div.flex.gap-4.items-center.my-2
(com/checkbox {"@click" "showTable=!showTable"} "Show table") (com/checkbox {"@click" "showTable=!showTable"} "Show table")
(com/checkbox {:x-model "errorsOnly"} "Only show errors")
(com/button {:color :primary :type "submit"} "Import")] (com/button {:color :primary :type "submit"} "Import")]
[:div {:x-show "showTable"} [:div {:x-show "showTable || errorsOnly"}
(com/data-grid-card (com/data-grid-card
{:id "transaction-import-data" {:id "transaction-import-data"
:route nil :route nil
@@ -204,8 +205,11 @@
(fc/cursor-map (fc/cursor-map
(fn [_] (fn [_]
(let [row-errors (fc/field-errors)] (let [row-errors (fc/field-errors)]
;; Clean rows are only hidden (x-show), never removed, so
;; "Only show errors" never changes what gets posted.
(com/data-grid-row (com/data-grid-row
{} (cond-> {}
(not (seq row-errors)) (assoc :x-show "!errorsOnly"))
(com/data-grid-cell {} (fc/with-field :raw-date (com/data-grid-cell {} (fc/with-field :raw-date
(com/text-input {:value (fc/field-value) :name (fc/field-name) :class "w-28"}))) (com/text-input {:value (fc/field-value) :name (fc/field-name) :class "w-28"})))
(com/data-grid-cell {} (fc/with-field :description-original (com/data-grid-cell {} (fc/with-field :description-original

View File

@@ -329,7 +329,7 @@
:request request} :request request}
(com/breadcrumbs {} (com/breadcrumbs {}
[:a {:href (bidi/path-for ssr-routes/only-routes [:a {:href (bidi/path-for ssr-routes/only-routes
::transaction-routes/all-page)} ::transaction-routes/page)}
"Transactions"] "Transactions"]
[:a {:href (bidi/path-for ssr-routes/only-routes [:a {:href (bidi/path-for ssr-routes/only-routes
:transaction-insights)} :transaction-insights)}

View File

@@ -334,7 +334,7 @@
(and (map? data) (and (map? data)
(every? #(try (Long/parseLong %) true (catch Exception _ false)) (keys data))) (every? #(try (Long/parseLong %) true (catch Exception _ false)) (keys data)))
(into [] (->> (keys data) (into [] (->> (keys data)
sort (sort-by #(Long/parseLong %))
(map data))) (map data)))
(nil? data) (nil? data)
nil nil

View File

@@ -267,12 +267,33 @@
account)) account))
accounts)))) accounts))))
(defn used-accounts [pnl-datas] (defn used-accounts
"One entry per numeric code in play, with the name to label its row.
Rows are keyed on the code alone, never on [code name]: the amount a row
reports is the total for its code, so two names at one code would print the
same figure twice and the section would stop footing to its own subtotal.
`build-account-lookup` has already resolved a client's shared codes down to
the bank account's name, so the name here is normally unanimous. It can
still differ across a multi-client report, where only some of the clients
have a bank account at the code. A bank account's name wins there; failing
that the most common name does, ties broken alphabetically so the report is
stable across runs."
[pnl-datas]
(->> (->>
pnl-datas pnl-datas
(mapcat :data) (mapcat :data)
(map #(select-keys % [:numeric-code :name])) (map #(select-keys % [:numeric-code :name :bank-account-name?]))
(set) (group-by :numeric-code)
(map (fn [[numeric-code entries]]
{:numeric-code numeric-code
:name (->> (or (seq (filter :bank-account-name? entries))
entries)
(map :name)
frequencies
(sort-by (juxt (comp - val) key))
ffirst)}))
(sort-by :numeric-code))) (sort-by :numeric-code)))
(defn subtotal-by-column-row [pnl-datas title & [cell-args]] (defn subtotal-by-column-row [pnl-datas title & [cell-args]]
@@ -477,11 +498,11 @@
(map (map
(fn [p] (fn [p]
(let [pnl-data (-> p (filter-numeric-code numeric-code numeric-code)) (let [pnl-data (-> p (filter-numeric-code numeric-code numeric-code))
this-name-exists? (->> (:data p) ;; Code, not name: the row is keyed on the code, and a
(filter (comp #{name} :name)) ;; client can reach it under a name another client won.
seq)] this-code-exists? (seq (:data pnl-data))]
(merge (merge
(if this-name-exists? (if this-code-exists?
{:format :dollar {:format :dollar
:filters (:filters pnl-data) :filters (:filters pnl-data)
:value (aggregate-accounts pnl-data)} :value (aggregate-accounts pnl-data)}

View File

@@ -2,6 +2,7 @@
(def routes {"" {:get ::page (def routes {"" {:get ::page
:put ::edit-wizard-submit} :put ::edit-wizard-submit}
"/table" ::table "/table" ::table
"/csv" ::csv
["/" [#"\d+" :db/id]] {:get ::edit-wizard} ["/" [#"\d+" :db/id]] {:get ::edit-wizard}
"/edit/navigate" ::edit-wizard-navigate "/edit/navigate" ::edit-wizard-navigate
"/edit/sales-summary-item" ::new-summary-item "/edit/sales-summary-item" ::new-summary-item

View File

@@ -0,0 +1,129 @@
(ns auto-ap.ledger.reports-test
(:require
[auto-ap.ledger.reports :as sut]
[clojure.test :refer [deftest is testing]]))
;; A client's bank account and the financial account it posts to share a
;; numeric code. Reports key their rows off that code, so before the fix the
;; pair rendered as two rows carrying the same code-level total, and the
;; section stopped footing to its own subtotal.
(def ^:private period #inst "2026-08-14")
(defn- account
[numeric-code name amount]
{:client-id 1
:location "M"
:numeric-code numeric-code
:name name
:amount amount
:debits 0.0
:credits amount
:count 1
:account-type :account-type/liability
:period period})
(defn- bank-account
"Same as `account`, but its name came from a :bank-account rather than the
chart of accounts — which is what earns it the row label."
[numeric-code name amount]
(assoc (account numeric-code name amount) :bank-account-name? true))
(defn- pnl-data
[data]
(sut/->PNLData {:periods [period]} data {1 "CLIENT"}))
(defn- labels
[rows]
(map (comp :value first) rows))
(deftest used-accounts-collapses-a-shared-numeric-code
(testing "two names on one code yield a single entry, named for the bank account"
(is (= [{:numeric-code 21010 :name "Capital One CC - 3196"}]
(sut/used-accounts
[{:data [(bank-account 21010 "Capital One CC - 3196" -100.0)
(account 21010 "Accounts Payable 10" -150.0)]}]))))
(testing "the bank account wins even when the financial name is more common"
(is (= [{:numeric-code 21010 :name "Capital One CC - 3196"}]
(sut/used-accounts
[{:data [(bank-account 21010 "Capital One CC - 3196" -100.0)]}
{:data [(account 21010 "Accounts Payable 10" -150.0)]}
{:data [(account 21010 "Accounts Payable 10" -150.0)]}]))))
(testing "distinct codes are left alone, ordered by code"
(is (= [{:numeric-code 21009 :name "Due to Grand Ventures"}
{:numeric-code 21010 :name "Capital One CC - 3196"}]
(sut/used-accounts
[{:data [(bank-account 21010 "Capital One CC - 3196" -100.0)
(account 21009 "Due to Grand Ventures" 25.0)]}]))))
(testing "with no bank account in play the most common name wins"
(is (= [{:numeric-code 21010 :name "Accounts Payable 10"}]
(sut/used-accounts
[{:data [(account 21010 "Due to Sandwich Monkey" -100.0)]}
{:data [(account 21010 "Accounts Payable 10" -150.0)]}
{:data [(account 21010 "Accounts Payable 10" -150.0)]}]))))
(testing "an even split breaks alphabetically so runs are reproducible"
(is (= [{:numeric-code 21010 :name "Accounts Payable 10"}]
(sut/used-accounts
[{:data [(account 21010 "Due to Sandwich Monkey" -100.0)]}
{:data [(account 21010 "Accounts Payable 10" -150.0)]}])))
(is (= [{:numeric-code 21010 :name "Amex - 41001"}]
(sut/used-accounts
[{:data [(bank-account 21010 "BofA CC - 8779" -100.0)]}
{:data [(bank-account 21010 "Amex - 41001" -150.0)]}]))
"two bank accounts still resolve to one, deterministically")))
(deftest balance-sheet-renders-one-row-per-shared-code
(let [report (sut/summarize-balance-sheet
(pnl-data [(bank-account 21010 "Capital One CC - 3196" -100.0)
(account 21010 "Accounts Payable 10" -150.0)
(account 21009 "Due to Grand Ventures" 25.0)]))
rows (:rows report)]
(testing "the shared code appears once, under the bank account's name"
(is (= 1 (count (filter #(= "Capital One CC - 3196:21010" %) (labels rows)))))
(is (not (some #{"Accounts Payable 10:21010"} (labels rows)))))
(testing "its row carries the total for the code, not one account's share"
(is (= -250.0
(->> rows
(filter #(= "Capital One CC - 3196:21010" (:value (first %))))
first
second
:value))))
(testing "the detail rows foot to the section subtotal"
(let [detail (->> rows
(filter #(re-find #":\d+$" (str (:value (first %)))))
(map (comp :value second)))
subtotal (->> rows
(filter #(= "Liabilities" (:value (first %))))
(keep (comp :value second))
last)]
(is (= 2 (count detail))
"three accounts across two codes collapse to two rows")
(is (= -225.0 (reduce + 0.0 detail) subtotal)
"before the fix 21010 printed twice and the rows overshot the subtotal")))))
(deftest balance-sheet-keeps-a-clients-figure-when-another-client-named-the-code
(testing "a column is blank only when that client has nothing at the code"
(let [rows (:rows (sut/summarize-balance-sheet
(sut/->PNLData
{:periods [period]}
[(assoc (bank-account 21010 "Capital One CC - 3196" -100.0) :client-id 1)
(assoc (account 21010 "Accounts Payable 10" -150.0) :client-id 2)
(assoc (account 21009 "Due to Grand Ventures" 25.0) :client-id 1)]
{1 "ONE" 2 "TWO"})))
shared (->> rows
(filter #(re-find #":21010$" (str (:value (first %)))))
first)
absent (->> rows
(filter #(re-find #":21009$" (str (:value (first %)))))
first)]
(is (= [-100.0 -150.0] (map :value (drop-last (rest shared))))
"client TWO reaches 21010 under a name client ONE won, and still shows its balance")
(is (= [25.0 ""] (map :value (drop-last (rest absent))))
"client TWO has nothing at 21009 and stays blank"))))

View File

@@ -1,10 +1,98 @@
(ns auto-ap.ledger-test (ns auto-ap.ledger-test
(:require (:require
[auto-ap.integration.util :refer [wrap-setup]] [auto-ap.datomic :refer [conn]]
[clojure.test :as t])) [auto-ap.integration.util :refer [test-account test-bank-account test-client
wrap-setup]]
[auto-ap.ledger :refer [build-account-lookup]]
[clojure.test :as t]
[datomic.api :as dc]))
(t/use-fixtures :each wrap-setup) (t/use-fixtures :each wrap-setup)
(t/deftest build-account-lookup-prefers-the-bank-account-name
(t/testing "a bank account and the financial account sharing its code resolve to one name"
(let [{:strs [client bank financial]}
(:tempids @(dc/transact conn
[(test-account :db/id "financial"
:account/name "Accounts Payable 10"
:account/numeric-code 21010
:account/type :account-type/liability)
(test-client :db/id "client"
:client/bank-accounts
[(test-bank-account :db/id "bank"
:bank-account/name "Capital One CC - 3196"
:bank-account/numeric-code 21010
:bank-account/type :bank-account-type/credit)])]))
lookup (build-account-lookup client)]
(t/is (= "Capital One CC - 3196" (:name (lookup bank))))
(t/is (= "Capital One CC - 3196" (:name (lookup financial)))
"the financial account takes the bank account's name, so reports render one row")
(t/testing "and both are flagged as bank-named for downstream report labelling"
(t/is (true? (:bank_account_name? (lookup bank))))
(t/is (true? (:bank_account_name? (lookup financial)))))
(t/testing "without changing the code or the account type"
(t/is (= 21010 (:numeric_code (lookup bank))))
(t/is (= 21010 (:numeric_code (lookup financial))))
(t/is (= :account-type/liability (:account_type (lookup financial))))))))
(t/deftest build-account-lookup-leaves-unshared-codes-alone
(t/testing "an account with no bank account at its code keeps its own name"
(let [{:strs [client financial]}
(:tempids @(dc/transact conn
[(test-account :db/id "financial"
:account/name "Sales Taxes Payable"
:account/numeric-code 23000
:account/type :account-type/liability)
(test-client :db/id "client"
:client/bank-accounts
[(test-bank-account :db/id "bank"
:bank-account/name "Capital One CC - 3196"
:bank-account/numeric-code 21010
:bank-account/type :bank-account-type/credit)])]))
lookup (build-account-lookup client)]
(t/is (= "Sales Taxes Payable" (:name (lookup financial))))
(t/is (false? (:bank_account_name? (lookup financial))))))
(t/testing "another client's bank account never renames this client's accounts"
(let [{:strs [mine financial]}
(:tempids @(dc/transact conn
[(test-account :db/id "financial"
:account/name "Accounts Payable 10"
:account/numeric-code 21010
:account/type :account-type/liability)
(test-client :db/id "mine")
(test-client :db/id "theirs"
:client/bank-accounts
[(test-bank-account :db/id "bank"
:bank-account/name "Capital One CC - 3196"
:bank-account/numeric-code 21010
:bank-account/type :bank-account-type/credit)])]))
lookup (build-account-lookup mine)]
(t/is (= "Accounts Payable 10" (:name (lookup financial))))
(t/is (false? (:bank_account_name? (lookup financial)))))))
(t/deftest build-account-lookup-breaks-ties-between-bank-accounts
(t/testing "two bank accounts on one code resolve deterministically by sort-order"
(let [{:strs [client first-bank second-bank]}
(:tempids @(dc/transact conn
[(test-client :db/id "client"
:client/bank-accounts
[(test-bank-account :db/id "second-bank"
:bank-account/name "US Bank 2974"
:bank-account/numeric-code 13101
:bank-account/sort-order 3)
(test-bank-account :db/id "first-bank"
:bank-account/name "Fremont Gyro HB Main 8576"
:bank-account/numeric-code 13101
:bank-account/sort-order 0)])]))
lookup (build-account-lookup client)]
(t/is (= "Fremont Gyro HB Main 8576"
(:name (lookup first-bank))
(:name (lookup second-bank)))))))
(t/deftest entity-change->ledger (t/deftest entity-change->ledger
#_(t/testing "Should code an expected deposit" #_(t/testing "Should code an expected deposit"
(let [{:strs [ed ccp receipts-split client]} (let [{:strs [ed ccp receipts-split client]}

View File

@@ -70,3 +70,66 @@
(is (= "NICK THE GREEK" (:customer-identifier result))) (is (= "NICK THE GREEK" (:customer-identifier result)))
(is (= "600 VISTA WAY" (str/trim (:account-number result)))) (is (= "600 VISTA WAY" (str/trim (:account-number result))))
(is (= "946.24" (:total result))))))) (is (= "946.24" (:total result)))))))
(deftest parse-bonanza-produce-invoice-03932070
(testing "Should parse a Bonanza invoice whose bill-to block carries a mixed-case, punctuated street address"
(let [pdf-file (io/file "dev-resources/Bonanza Sample Reno 2.pdf")
pdf-text (:out (clojure.java.shell/sh "pdftotext" "-layout" (str pdf-file) "-"))
results (sut/parse pdf-text)
result (first results)]
(is (some? result) "Template should match and return a result")
(when result
(is (= "Bonanza Produce" (:vendor-code result)))
(is (= "03932070" (:invoice-number result)))
(let [d (:date result)]
(is (= 2026 (time/year d)))
(is (= 8 (time/month d)))
(is (= 4 (time/day d))))
;; "10310 N McCARRAN BLVD STE.400" has both lowercase letters and a
;; period; an uppercase/digit-only capture used to drop it entirely,
;; leaving the invoice with no identifier to match a client against.
(is (= "10310 N McCARRAN BLVD STE.400" (str/trim (:account-number result))))
(is (str/starts-with? (:customer-identifier result) "NICK THE GREEK"))
(is (str/includes? (:customer-identifier result) "McCARRAN"))
(is (= "524.17" (:total result)))))))
(deftest parse-bonanza-produce-invoice-03933054
(testing "Should keep parsing the sibling Reno location whose address was already extractable"
(let [pdf-file (io/file "dev-resources/Bonanza Sample Reno.pdf")
pdf-text (:out (clojure.java.shell/sh "pdftotext" "-layout" (str pdf-file) "-"))
results (sut/parse pdf-text)
result (first results)]
(is (some? result) "Template should match and return a result")
(when result
(is (= "Bonanza Produce" (:vendor-code result)))
(is (= "03933054" (:invoice-number result)))
(let [d (:date result)]
(is (= 2026 (time/year d)))
(is (= 8 (time/month d)))
(is (= 7 (time/day d))))
(is (= "5140 KIETZKE" (str/trim (:account-number result))))
(is (str/starts-with? (:customer-identifier result) "NICK THE GREEK"))
(is (str/includes? (:customer-identifier result) "KIETZKE"))
;; Two-page invoice: the totals only appear on the final page.
(is (= "955.75" (:total result)))))))
(deftest parse-reel-produce-statement-28676
(testing "Should parse the Reel Produce statement layout that no longer prints 'Reel Produce' on the page"
(let [pdf-file (io/file "dev-resources/Statement1_from_REEL_Produce_Inc.28676.pdf")
pdf-text (:out (clojure.java.shell/sh "pdftotext" "-layout" (str pdf-file) "-"))
results (sut/parse pdf-text)]
(is (seq results) "Template should match and return results")
(is (= 7 (count results)) "Should parse 7 invoices from statement")
(doseq [result results]
(is (= "Reel Produce" (:vendor-code result)))
(is (= "Sushi Confidential - San Jose" (:customer-identifier result))))
(is (= ["454379" "454826" "455120" "455683" "456654" "456774" "457171"]
(mapv :invoice-number results)))
(is (= ["1003.10" "530.85" "605.00" "1187.40" "164.00" "675.60" "265.75"]
(mapv :total results)))
;; totals add up to the statement's $4,431.70 amount due
(is (= 4431.70 (->> results (map #(Double/parseDouble (:total %))) (reduce +))))
(let [d (:date (first results))]
(is (= 2026 (time/year d)))
(is (= 6 (time/month d)))
(is (= 23 (time/day d)))))))

View File

@@ -0,0 +1,40 @@
(ns auto-ap.query-params-test
(:require
[auto-ap.query-params :as sut]
[auto-ap.ssr.invoices :as invoices]
[auto-ap.ssr.transaction.common :as transaction]
[clojure.edn :as edn]
[clojure.test :refer [deftest is testing]]))
(defn- sortable-keys [grid]
(->> (:headers grid) (keep :sort-key)))
(deftest parse-sort-round-trips-through-edn
;; The bulk wizards copy :query-params into their form snapshot, serialize it with
;; pr-str into a hidden field, and read it back with clojure.edn/read-string on
;; submit. Anything parse-sort puts in :query-params has to survive that trip --
;; a bare fn or other unprintable object pr-strs as #object[...], which edn cannot
;; read, and the submit 500s before the handler ever runs.
(doseq [[label grid] [["transactions" transaction/grid-page]
["invoices" invoices/grid-page]]
sort-key (sortable-keys grid)
direction ["asc" "desc"]]
(testing (str label " sorted by " sort-key ":" direction)
(let [parsed (sut/parse-sort grid (str sort-key ":" direction))]
(is (seq parsed) "should produce a sort entry")
(is (= parsed (edn/read-string (pr-str parsed)))
"parsed sort must survive a pr-str / edn round trip")))))
(deftest parse-sort-behaviour
(testing "Unknown columns are dropped"
(is (= [] (sut/parse-sort transaction/grid-page "not-a-column:asc"))))
(testing "An empty sort query yields no sort"
(is (= [] (sut/parse-sort transaction/grid-page ""))))
(testing "Multiple sort keys are preserved in order"
(is (= ["client" "vendor"]
(mapv :sort-key (sut/parse-sort transaction/grid-page "client:asc,vendor:desc")))))
(testing "Direction is parsed per key"
(is (= [true false]
(mapv :asc (sut/parse-sort transaction/grid-page "client:asc,vendor:desc")))))
(testing "The display name is carried over from the matching header"
(is (= "Date" (:name (first (sut/parse-sort transaction/grid-page "date:asc")))))))

View File

@@ -0,0 +1,86 @@
(ns auto-ap.ssr.ledger.export-modal-test
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.integration.util :refer [admin-token test-client user-token
wrap-setup]]
[auto-ap.ssr.ledger.export-modal :as sut]
[clojure.string :as str]
[clojure.test :refer [deftest is testing use-fixtures]]
[datomic.api :as dc]))
(use-fixtures :each wrap-setup)
(def report {:report/name "Profit-and-loss-2026-07-31-for-Acme"
:report/url "https://data.example.com/reports/pnl/abc/report.pdf"})
(defn client-with-emails
"Transacts a client with `emails` contacts. :client/emails is a plain ref, not
a component, so each nested contact needs its own tempid."
[emails]
(let [suffix (rand-int 1000000)
client-tid (str "client-" suffix)
contacts (map-indexed (fn [i e] (assoc e :db/id (str "email-contact-" suffix "-" i)))
emails)
tempids (:tempids @(dc/transact conn
(into [(test-client :db/id client-tid
:client/emails (map :db/id contacts))]
contacts)))]
{:db/id (get tempids client-tid)}))
(defn modal-body [request report]
(:body (sut/export-modal request report)))
(deftest export-modal-download-link
(testing "Should always offer the report for download"
(let [client (client-with-emails [])
body (modal-body {:identity (admin-token)}
(assoc report :report/clients [client]))]
(is (str/includes? body (:report/url report))
"the S3 url should be linked")
(is (str/includes? body "Download PDF")))))
(deftest export-modal-email-handoff
(testing "Should offer a mailto hand-off addressed to the client's contacts"
(let [client (client-with-emails [{:email-contact/email "owner@acme.com"
:email-contact/description "Owner"}
{:email-contact/email "cpa@acme.com"}])
body (modal-body {:identity (admin-token)}
(assoc report :report/clients [client]))]
(is (str/includes? body "mailto:"))
(is (str/includes? body "owner@acme.com"))
(is (str/includes? body "cpa@acme.com"))
(is (str/includes? body "Owner")
"contacts should be described so the sender can see who it goes to")))
(testing "Should not offer a hand-off when the client has no email contacts"
(let [client (client-with-emails [])
body (modal-body {:identity (admin-token)}
(assoc report :report/clients [client]))]
(is (not (str/includes? body "mailto:")))))
(testing "Should not offer a hand-off to non-admins"
(let [client (client-with-emails [{:email-contact/email "owner@acme.com"}])
body (modal-body {:identity (user-token (:db/id client))}
(assoc report :report/clients [client]))]
(is (not (str/includes? body "mailto:")))))
(testing "Should not offer a hand-off for a multi-client report"
(let [a (client-with-emails [{:email-contact/email "owner@acme.com"}])
b (client-with-emails [{:email-contact/email "owner@beta.com"}])
body (modal-body {:identity (admin-token)}
(assoc report :report/clients [a b]))]
(is (not (str/includes? body "mailto:"))
"one client's numbers must not be mailed to another client's contacts"))))
(deftest mailto-href-encoding
(let [contacts [{:email-contact/email "owner@acme.com"}
{:email-contact/email "cpa@acme.com"}]
href (sut/mailto-href contacts report)]
(testing "Should address every contact"
(is (str/starts-with? href "mailto:owner@acme.com,cpa@acme.com?")))
(testing "Should percent-encode the subject and body"
(is (str/includes? href "subject=Profit-and-loss-2026-07-31-for-Acme%20is%20ready"))
(is (not (re-find #"[\s\n]" href))
"raw whitespace would truncate the href"))
(testing "Should carry the report url in the body"
(is (str/includes? (sut/email-body (:report/url report)) (:report/url report))))))

View File

@@ -13,6 +13,7 @@
[clojure.data.csv :as csv] [clojure.data.csv :as csv]
[clojure.string :as str] [clojure.string :as str]
[datomic.api :as dc] [datomic.api :as dc]
[hiccup2.core :as hiccup]
[malli.core :as mc])) [malli.core :as mc]))
(use-fixtures :each wrap-setup) (use-fixtures :each wrap-setup)
@@ -369,6 +370,55 @@
(is (= "TEST" (:client-code (first entries)))) (is (= "TEST" (:client-code (first entries))))
(is (= 2 (count (:line-items (first entries)))))))) (is (= 2 (count (:line-items (first entries))))))))
(deftest table->entries-blank-amount-rows-test
(let [line (fn [external-id debit credit account-code]
{:source "manual"
:client-code "TEST"
:external-id external-id
:date (coerce/to-date-time #inst "2021-01-01")
:vendor-name "Vendor"
:debit debit
:credit credit
:account-code account-code
:location "HQ"})
entries (fn [table]
(sut/table->entries table
{"Vendor" {:db/id "vendor-1"}}
#{"1100" "1101"}
{"TEST" #inst "2000-01-01"}
{"TEST" #{}}
{"TEST" #{"HQ"}}))]
(testing "Should skip rows with no debit and no credit without flagging them"
(let [_ (setup-test-data [(test-client :db/id "blank-client-1"
:client/code "TEST"
:client/locations ["HQ"])])
result (entries [(line "ext-blank-1" 100.0 nil 1100)
(line "ext-blank-1" nil nil 1101)
(line "ext-blank-1" nil 100.0 1101)])]
(is (= 1 (count result)))
(is (= 2 (count (:line-items (first result)))))
(is (= [0 2] (vec (:indices (first result)))))
(is (= 100.0 (:amount (first result))))
(is (empty? (sut/entry-errors (first result))))))
(testing "Should treat blank strings in the amount columns as no amount"
(let [_ (setup-test-data [(test-client :db/id "blank-client-2"
:client/code "TEST"
:client/locations ["HQ"])])
result (entries [(line "ext-blank-2" 100.0 nil 1100)
(line "ext-blank-2" "" " " 1101)
(line "ext-blank-2" nil 100.0 1101)])]
(is (= 2 (count (:line-items (first result)))))
(is (empty? (sut/entry-errors (first result))))))
(testing "Should drop an entry entirely when every one of its rows is blank"
(let [_ (setup-test-data [(test-client :db/id "blank-client-3"
:client/code "TEST"
:client/locations ["HQ"])])
result (entries [(line "ext-blank-3" nil nil 1100)
(line "ext-blank-3" nil nil 1101)])]
(is (empty? result))))))
(deftest import-ledger-test (deftest import-ledger-test
(testing "Should upsert hidden vendors and create transactions" (testing "Should upsert hidden vendors and create transactions"
(let [_ (setup-test-data [(test-client :db/id "import-client-1" (let [_ (setup-test-data [(test-client :db/id "import-client-1"
@@ -396,7 +446,44 @@
(let [vendor-id (dc/q '[:find ?e . (let [vendor-id (dc/q '[:find ?e .
:where [?e :vendor/name "New Vendor Import Unique"]] :where [?e :vendor/name "New Vendor Import Unique"]]
db-after)] db-after)]
(is vendor-id))))) (is vendor-id))))
(testing "Should import the entry while ignoring its rows with no debit and no credit"
(let [_ (setup-test-data [(test-client :db/id "import-client-2"
:client/code "IMPORT-BLANK"
:client/locations ["HQ"])
{:db/id "import-blank-account-1100"
:account/numeric-code 1100
:account/account-set "default"
:account/name "Cash"}
{:db/id "import-blank-account-1101"
:account/numeric-code 1101
:account/account-set "default"
:account/name "Other Cash"}])
row (fn [debit credit account-code]
{:source "manual"
:client-code "IMPORT-BLANK"
:external-id "ext-import-blank-1"
:date (coerce/to-date-time #inst "2021-01-01")
:vendor-name "Blank Row Vendor"
:debit debit
:credit credit
:account-code account-code
:location "HQ"})
result (sut/import-ledger {:form-params {:table [(row 100.0 nil 1100)
(row nil nil 1101)
(row nil 100.0 1101)]}
:identity (admin-token)})
entry (dc/pull (dc/db conn)
[:journal-entry/amount
{:journal-entry/line-items [:journal-entry-line/debit
:journal-entry-line/credit]}]
[:journal-entry/external-id "IMPORT-BLANK-manual-ext-import-blank-1"])]
(is (= 1 (:successful result)))
(is (= 0 (:ignored result)))
(is (empty? (:form-errors result)))
(is (= 100.0 (:journal-entry/amount entry)))
(is (= 2 (count (:journal-entry/line-items entry)))))))
(deftest import-ledger-with-errors-test (deftest import-ledger-with-errors-test
(testing "Should throw exception when entries have errors - client not found" (testing "Should throw exception when entries have errors - client not found"
@@ -619,3 +706,176 @@
(testing "Non-admin cannot bulk-delete" (testing "Non-admin cannot bulk-delete"
(is (thrown? Exception (sut/bulk-delete {:identity (user-token) (is (thrown? Exception (sut/bulk-delete {:identity (user-token)
:form-params {:selected [1]}}))))) :form-params {:selected [1]}})))))
;; =============================================================================
;; External Import - removing an entry from the review grid
;; =============================================================================
(defn- import-row [external-id account-code debit credit]
{:external-id external-id
:client-code "REMOVE-TEST"
:source "manual"
:vendor-name "Remove Vendor"
:date (coerce/to-date-time #inst "2021-01-01")
:account-code account-code
:location "HQ"
:debit debit
:credit credit})
(defn- entry-rows
"The two rows a balanced ledger entry is typically pasted as."
[external-id debit-account amount]
[(import-row external-id debit-account amount 0.0)
(import-row external-id 2000 0.0 amount)])
(deftest external-import-remove-button-test
(testing "Every row is tagged with the entry id the importer groups on"
(let [table (vec (concat (entry-rows "ext-a" 1100 100.0)
(entry-rows "ext-b" 1100 50.0)))
html (str (hiccup/html (sut/external-import-table-form*
{:form-params {:table table}
:form-errors {}})))]
(is (= 2 (count (re-seq #"data-entry-id=\"REMOVE-TEST-manual-ext-a\"" html))))
(is (= 2 (count (re-seq #"data-entry-id=\"REMOVE-TEST-manual-ext-b\"" html))))
(testing "and offers a remove button that drops the whole entry"
(is (= 4 (count (re-seq #"\$dispatch\('remove-import-entry'" html))))
(is (= 4 (count (re-seq #"remove-import-entry\.window" html))))
(is (= 2 (count (re-seq #"data-remove-entry-id=\"REMOVE-TEST-manual-ext-a\"" html))))
(is (= 2 (count (re-seq #"aria-label=\"Remove ledger entry REMOVE-TEST-manual-ext-a\"" html))))))))
(deftest external-import-remove-entry-then-import-test
(testing "Removing the failing entry lets the remaining entries import"
(let [_ (setup-test-data [(test-client :db/id "remove-client"
:client/code "REMOVE-TEST"
:client/locations ["HQ"])
(test-vendor :db/id "remove-vendor"
:vendor/name "Remove Vendor")
{:db/id "remove-account-1100"
:account/numeric-code 1100
:account/account-set "default"
:account/name "Cash"}
{:db/id "remove-account-2000"
:account/numeric-code 2000
:account/account-set "default"
:account/name "Accounts Payable"}])
;; Three entries, six rows. The middle one posts to an account that
;; does not exist, which is the kind of error a user has to resolve.
table (vec (concat (entry-rows "ext-good-1" 1100 100.0)
(entry-rows "ext-bad" 99999 75.0)
(entry-rows "ext-good-2" 1100 25.0)))
admin (admin-token)
external-id (fn [id] (str "REMOVE-TEST-manual-" id))
imported? (fn [db id]
(boolean (dc/q '[:find ?je .
:in $ ?ext
:where [?je :journal-entry/external-id ?ext]]
db (external-id id))))]
(testing "the bad entry blocks the whole paste"
(is (thrown? Exception (sut/import-ledger {:form-params {:table table}
:identity admin})))
(let [db (dc/db conn)]
(is (not (imported? db "ext-good-1")))
(is (not (imported? db "ext-good-2")))))
(testing "removing it drops both of its rows"
(let [remaining (vec (remove #(= (external-id "ext-bad") (sut/line->id %)) table))]
(is (= 4 (count remaining)))
(testing "and the other two entries import"
(let [result (sut/import-ledger {:form-params {:table remaining}
:identity admin})
db (dc/db conn)]
(is (= 2 (:successful result)))
(is (imported? db "ext-good-1"))
(is (imported? db "ext-good-2"))
(is (not (imported? db "ext-bad")))
(let [entry (dc/pull db
[:journal-entry/amount
{:journal-entry/line-items [:journal-entry-line/debit
:journal-entry-line/credit]}]
[:journal-entry/external-id (external-id "ext-good-1")])]
(is (= 100.0 (:journal-entry/amount entry)))
(is (= 2 (count (:journal-entry/line-items entry))))))))))))
(deftest fetch-ids-include-in-reports-test
(testing "Should leave an entry out of the register when a line posts to a bank account off reports"
(let [{:strs [reports-client shown-entry]}
(setup-test-data [(test-bank-account :db/id "shown-bank"
:bank-account/name "Shown Bank"
:bank-account/numeric-code 11000
:bank-account/include-in-reports true)
(test-bank-account :db/id "hidden-bank"
:bank-account/name "Hidden Bank"
:bank-account/numeric-code 11001
:bank-account/include-in-reports false)
(test-client :db/id "reports-client"
:client/code "REPORTS-TEST"
:client/locations ["HQ"]
:client/bank-accounts ["shown-bank" "hidden-bank"])
{:db/id "reports-expense"
:account/name "Expense"
:account/numeric-code 60000
:account/account-set "default"}
{:db/id "shown-entry"
:journal-entry/client "reports-client"
:journal-entry/date #inst "2024-03-01"
:journal-entry/amount 100.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "shown-bank"
:journal-entry-line/credit 100.0}
{:journal-entry-line/account "reports-expense"
:journal-entry-line/debit 100.0}]}
{:db/id "hidden-entry"
:journal-entry/client "reports-client"
:journal-entry/date #inst "2024-03-02"
:journal-entry/amount 50.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "hidden-bank"
:journal-entry-line/credit 50.0}
{:journal-entry-line/account "reports-expense"
:journal-entry-line/debit 50.0}]}])
request {:client-id reports-client
:clients [reports-client]
:route-params {}
:query-params {}}
result (common/fetch-ids (dc/db conn) request)]
(is (= [shown-entry] (vec (:all-ids result))))
(is (= [shown-entry] (vec (:ids result))))
(testing "and counts it out of the total, so the page is not silently short"
(is (= 1 (:count result))))))
(testing "Should keep every entry when no bank account is off reports"
(let [{:strs [open-client open-entry]}
(setup-test-data [(test-bank-account :db/id "open-bank"
:bank-account/name "Open Bank"
:bank-account/numeric-code 11002
:bank-account/include-in-reports true)
(test-client :db/id "open-client"
:client/code "REPORTS-OPEN"
:client/locations ["HQ"]
:client/bank-accounts ["open-bank"])
{:db/id "open-expense"
:account/name "Expense"
:account/numeric-code 60001
:account/account-set "default"}
{:db/id "open-entry"
:journal-entry/client "open-client"
:journal-entry/date #inst "2024-03-01"
:journal-entry/amount 100.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "open-bank"
:journal-entry-line/credit 100.0}
{:journal-entry-line/account "open-expense"
:journal-entry-line/debit 100.0}]}])
result (common/fetch-ids (dc/db conn) {:client-id open-client
:clients [open-client]
:route-params {}
:query-params {}})]
(is (= [open-entry] (vec (:all-ids result))))
(is (= 1 (:count result))))))

View File

@@ -0,0 +1,68 @@
(ns auto-ap.ssr.transaction.common-test
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.integration.util :refer [setup-test-data test-bank-account test-client
test-transaction wrap-setup]]
[auto-ap.ssr.transaction.common :as sut]
[clojure.test :refer [deftest is testing use-fixtures]]
[datomic.api :as dc]))
(use-fixtures :each wrap-setup)
(defn- fetch
"Runs the transactions page query the way the page does, over a range wide
enough to cover the fixture data."
[client-id & {:keys [route-params query-params]}]
(sut/fetch-ids (dc/db conn)
{:clients [client-id]
:route-params (or route-params {})
:query-params (merge {:start-date "2021-01-01"
:end-date "2023-12-31"}
query-params)}))
(deftest fetch-ids-suppressed-test
(let [{:strs [suppressed-client kept-tx suppressed-tx]}
(setup-test-data
[(test-bank-account :db/id "suppressed-bank")
(test-client :db/id "suppressed-client"
:client/bank-accounts ["suppressed-bank"])
(test-transaction :db/id "kept-tx"
:transaction/client "suppressed-client"
:transaction/bank-account "suppressed-bank"
:transaction/date #inst "2022-06-01"
:transaction/amount 100.0
:transaction/approval-status :transaction-approval-status/approved)
(test-transaction :db/id "suppressed-tx"
:transaction/client "suppressed-client"
:transaction/bank-account "suppressed-bank"
:transaction/date #inst "2022-06-02"
:transaction/amount 250.0
:transaction/approval-status :transaction-approval-status/suppressed)])]
(testing "Should leave a suppressed transaction out of the transactions page"
(let [{:keys [ids all-ids count]} (fetch suppressed-client)]
(is (= [kept-tx] (vec ids)))
(is (not (contains? (set all-ids) suppressed-tx))
"a suppressed transaction must not reach the amount total either")
(is (= 1 count)
"the row count has to match what the page renders, not the unfiltered scan")))
(testing "Should still list a transaction on an explicit status route"
(let [{:keys [ids]} (fetch suppressed-client
:route-params {:status :transaction-approval-status/approved})]
(is (= [kept-tx] (vec ids)))))
(testing "Should return nothing for a client whose transactions are all suppressed"
(let [{:strs [empty-client]}
(setup-test-data
[(test-bank-account :db/id "empty-bank")
(test-client :db/id "empty-client"
:client/bank-accounts ["empty-bank"])
(test-transaction :db/id "only-tx"
:transaction/client "empty-client"
:transaction/bank-account "empty-bank"
:transaction/date #inst "2022-06-03"
:transaction/approval-status :transaction-approval-status/suppressed)])
{:keys [ids count]} (fetch empty-client)]
(is (empty? ids))
(is (= 0 count))))))