7 Commits

Author SHA1 Message Date
3715910029 adds invoices dates 2026-05-23 12:28:21 -07:00
03bfca35cb Fix bulk code vendor pre-population for single vs multi-client contexts
- vendor-default-account now uses raw vendor default account (not client-specific override)
- Account name is clientized via d-accounts/clientize only for single-client contexts
- Added single-client-id helper that returns client ID only when user has exactly one client
- Added multi-client e2e test verifying no pre-population across multiple clients
- Updated test server to support multi-client mode switching via /test-set-client-mode
- Test server now seeds a second client for multi-client scenarios
2026-05-23 11:21:22 -07:00
ba87805d4c Add vendor pre-population for bulk code and individual edit forms
- Add vendor-changed HTMX handlers for both bulk code and individual edit
- Pre-populate default account at 100% when vendor is selected and no accounts exist
- Fix render-accounts-section to render from step-params correctly
- Change bulk code vendor-changed from hx-get to hx-post to include form data
- Add routes for vendor-changed endpoints
- Update e2e tests to cover vendor pre-population
- Run lein cljfmt fix across codebase
2026-05-21 14:45:19 -07:00
8bd0cee1b1 Add e2e tests for bulk coding transactions and fix SSR location validation
- Create requirements document based on master cljs implementation
- Add Playwright e2e tests covering happy path, validation, and distribution
- Fix hiccup id syntax in SSR bulk code form (div#id.class order)
- Add missing account location validation to SSR bulk code submit
- Enhance test server with multiple transactions and fixed-location account
2026-05-21 13:21:22 -07:00
76c6eaddb9 improvements 2026-05-21 11:51:29 -07:00
ddf11a7cb3 Adds playwright 2026-05-21 11:50:50 -07:00
adb6ecb1ff stuff 2026-05-21 11:50:32 -07:00
219 changed files with 10065 additions and 9536 deletions

View File

@@ -0,0 +1,55 @@
---
name: agent-browser
description: Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.
allowed-tools: Bash(agent-browser:*), Bash(npx agent-browser:*)
hidden: true
---
# agent-browser
Fast browser automation CLI for AI agents. Chrome/Chromium via CDP with
accessibility-tree snapshots and compact `@eN` element refs.
Install: `npm i -g agent-browser && agent-browser install`
## Start here
This file is a discovery stub, not the usage guide. Before running any
`agent-browser` command, load the actual workflow content from the CLI:
```bash
agent-browser skills get core # start here — workflows, common patterns, troubleshooting
agent-browser skills get core --full # include full command reference and templates
```
The CLI serves skill content that always matches the installed version,
so instructions never go stale. The content in this stub cannot change
between releases, which is why it just points at `skills get core`.
## Specialized skills
Load a specialized skill when the task falls outside browser web pages:
```bash
agent-browser skills get electron # Electron desktop apps (VS Code, Slack, Discord, Figma, ...)
agent-browser skills get slack # Slack workspace automation
agent-browser skills get dogfood # Exploratory testing / QA / bug hunts
agent-browser skills get vercel-sandbox # agent-browser inside Vercel Sandbox microVMs
agent-browser skills get agentcore # AWS Bedrock AgentCore cloud browsers
```
Run `agent-browser skills list` to see everything available on the
installed version.
## Why agent-browser
- Fast native Rust CLI, not a Node.js wrapper
- Works with any AI agent (Cursor, Claude Code, Codex, Continue, Windsurf, etc.)
- Chrome/Chromium via CDP with no Playwright or Puppeteer dependency
- Accessibility-tree snapshots with element refs for reliable interaction
- Sessions, authentication vault, state persistence, video recording
- Specialized skills for Electron apps, Slack, exploratory testing, cloud providers
## Observability Dashboard
The dashboard runs independently of browser sessions on port 4848 and can also be opened through a proxied or forwarded URL such as `https://dashboard.agent-browser.localhost`. Agents should stay on the dashboard origin: session tabs, status, and stream traffic are proxied internally, so session ports do not need to be exposed.

3
.gitignore vendored
View File

@@ -46,3 +46,6 @@ data/solr/logs
.vscode/** .vscode/**
sysco-poller/**/*.csv sysco-poller/**/*.csv
.aider* .aider*
.tmp/**
playwright-report/**
test-results/**

144
AUTOMATION_NOTES.md Normal file
View File

@@ -0,0 +1,144 @@
# Automation Notes
Findings from investigating intermittent dialog-open failures on `/pos/summaries` (and likely other grid pages) when driven by `agent-browser`. Most of these apply equally to any browser automation — Playwright, Selenium, manual rapid-click testing.
## TL;DR
The reported "sometimes the dialog opens, sometimes it doesn't" was a server-side bug: `icon-button-` rendered as `<button>` with the HTML default `type="submit"`. Inside a `<form>` (every row in `grid_page_helper`), the click raced HTMX. If form submission won, the browser navigated to `/pos/summaries?id=…` and the modal request was canceled.
Fix is in `src/clj/auto_ap/ssr/components/buttons.clj``icon-button-` now defaults `:type "button"`. Verified with 30/30 rapid open/close cycles with random close delays spanning the entire 300 ms transition window.
## Modal lifecycle (for reference)
1. User clicks pencil → htmx `GET /pos/summaries/:id` (the edit-wizard route).
2. Server returns response with headers `hx-trigger: modalopen`, `hx-retarget: #modal-content`, `hx-reswap: innerHTML`. See `modal-response` in `src/clj/auto_ap/ssr/utils.clj:41`.
3. htmx swaps innerHTML of `#modal-content`, then dispatches a `modalopen` document event.
4. Alpine handler on `#modal-holder` (`src/clj/auto_ap/ssr/ui.clj:84`) sets `open=true`.
5. `x-show="open"` triggers a 300 ms enter transition on two nested divs (backdrop + content).
6. Closing dispatches `modalclose`, sets `open=false`, runs the 300 ms leave transition.
## Root cause of the reported flakiness
`grid_page_helper.clj:58-61` wraps each row's action buttons in a `<form>` with a hidden `id` field:
```clojure
(com/data-grid-right-stack-cell {}
(into [:form.flex.space-x-2
[:input {:type :hidden :name "id" :value ((:id-fn gridspec) entity)}]]
((:row-buttons gridspec) request entity)))
```
The buttons in `:row-buttons` come from `icon-button-`, which rendered `<button>` with no explicit type. HTML default: `type="submit"`. When the pencil is clicked:
- htmx normally intercepts via `hx-get` and calls `preventDefault()`.
- If anything (large DOM, htmx still initializing other elements, agent-browser issuing the click in a busy frame) delays htmx's listener relative to the form's submit handler, the form submits.
- Form submission triggers a same-page navigation to `/pos/summaries?id=<value>`, which cancels the in-flight XHR. The modal request never lands.
The race is non-deterministic, which is why it was intermittent. Browser automation makes it more visible because clicks fire faster than a human's, hitting moments when htmx might not yet have fully registered.
**Fix:** `icon-button-` now does `(merge {:type "button"} params)`. Same fix should be applied prophylactically to any other button helper used inside a row form: `button-`, `a-button-` (less relevant, `<a>` doesn't submit), `navigation-button-`. `group-button-` already sets `type="button"`. `validated-save-button-` correctly stays `submit`.
## Other findings (cosmetic — not causing failures)
### Duplicate `x-trap` directive
`src/clj/auto_ap/ssr/ui.clj:99-100`:
```clojure
"x-trap.inert.noscroll" "open"
"x-trap.inert" "open"
```
Both bound to the same expression. Alpine de-duplicates by directive name, so this is dead code. Drop the second line.
### Mixed `bg-opacity` and `opacity` in inner-modal transitions
`src/clj/auto_ap/ssr/ui.clj:103-107`:
```clojure
"x-transition:enter-start" "!bg-opacity-0 !translate-y-32"
"x-transition:enter-end" "!bg-opacity-100 !translate-y-0"
"x-transition:leave-start" "!opacity-100 !translate-y-0"
"x-transition:leave-end" "!opacity-0 !translate-y-32"
```
The inner div has no background color, so `bg-opacity-*` does nothing during enter. The leave correctly animates `opacity`. Net effect: enter is a translate-only animation while leave is translate-plus-fade. Asymmetric but works. Should be `opacity` on both sides for consistency.
### `_x_hidePromise` lingering flag
After rapid close→open cycles, Alpine's internal `_x_hidePromise` property remains truthy on the inner div even when the element is fully visible. It looks alarming when inspecting state but does not block subsequent transitions. Verified empirically with 30 trials.
## Browser-automation specifics
Things that aren't bugs but bite agent-browser scripts:
### Refs go stale on every HTMX swap
The `@eN` refs from a `snapshot` are valid only until the page changes. HTMX swaps innerHTML of `#modal-content` on every modal open, so any ref pointing inside the modal — or even refs pointing to row elements after the grid refreshes — silently breaks. Re-snapshot before each interaction; the docs explicitly warn about this.
### Default click is too fast for a busy frame
`agent-browser click @eN` dispatches a synthetic click via CDP without waiting for the page to settle. For htmx-driven interactions, the safe pattern is:
1. Click.
2. Wait for the observable side-effect, not for time.
For modal opens specifically:
```bash
agent-browser click @e_pencil
agent-browser wait --fn "document.querySelector('#modal-holder')?._x_dataStack?.[0]?.open && document.querySelector('#modal-content').children.length > 0"
agent-browser snapshot -i
```
For modal closes:
```bash
# After clicking a Save button that returns hx-trigger: modalclose
agent-browser wait --fn "!document.querySelector('#modal-holder')?._x_dataStack?.[0]?.open"
```
For grid refreshes after filter changes:
```bash
agent-browser wait --fn "!document.querySelector('.htmx-request')"
```
(htmx adds the `.htmx-request` class to elements during in-flight requests.)
### CDP screenshot timeouts
`agent-browser screenshot` occasionally returns `CDP command timed out: Page.captureScreenshot`. This is a Chromium/CDP issue, not application code. Workarounds:
- Don't rely on screenshots for state verification. Read state via `agent-browser eval` directly.
- If you need an image, retry once after a small wait.
### Reading Alpine state for diagnostics
Useful one-liners when debugging modal state:
```bash
agent-browser eval --stdin <<'EOF'
(()=>{
const h = document.querySelector('#modal-holder');
const c = document.querySelector('#modal-content');
const inner = c?.parentElement;
return {
open: h?._x_dataStack?.[0]?.open,
unexpectedError: h?._x_dataStack?.[0]?.unexpectedError,
contentChildren: c?.children.length,
innerDisplay: inner ? getComputedStyle(inner).display : null,
innerOpacity: inner ? getComputedStyle(inner).opacity : null,
hxRequest: !!document.querySelector('.htmx-request')
};
})()
EOF
```
## Patterns that improve reliability
When adding new interactive components:
- **Every `<button>` inside a form must declare `:type`**. Default to `"button"` for icon/utility buttons; only the actual submit needs `"submit"`. Either the component helper sets it or the call site does — never rely on the HTML default inside a form.
- **Don't dispatch `modalclose` and `modalopen` in the same tick.** They share `open` state and the result depends on order. If a flow needs to swap modals, use `modal-replace-response` (which sets `hx-trigger: modalswap` — see `src/clj/auto_ap/ssr/utils.clj:51`) so the swap goes through the `@modalswap.document` handler that explicitly sequences with `$nextTick`.
- **Prefer waiting on observable DOM/state over fixed delays.** `wait --fn` with an Alpine state check is faster and more reliable than `wait 500`.

View File

@@ -0,0 +1,85 @@
# Bulk Coding Transactions - Requirements Document
Based on analysis of the master cljs implementation (`src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs`) and GraphQL resolver (`src/clj/auto_ap/graphql/transactions.clj`).
## Feature Overview
Bulk coding allows admin users to apply vendor, approval status, and expense account allocations to multiple transactions simultaneously from the transactions grid page.
## Functional Requirements
### 1. Access Control
- **FR-1.1**: Bulk coding must be restricted to admin users only
- **FR-1.2**: The bulk code button should only be visible/enabled when transactions are selected
### 2. Transaction Selection
- **FR-2.1**: Users can select specific transactions via checkboxes in the grid
- **FR-2.2**: Users can select all visible transactions via a header checkbox
- **FR-2.3**: The system must filter out locked transactions (where client's `locked-until` date is after transaction date)
- **FR-2.4**: The modal must display the count of transactions that will actually be coded (after filtering locked ones)
### 3. Bulk Code Form Fields
- **FR-3.1**: **Vendor** (optional): Searchable typeahead to select a vendor
- **FR-3.2**: **Approval Status** (optional): Select from:
- No Change (empty)
- Approved
- Unapproved
- Suppressed
- Requires Feedback
- **FR-3.3**: **Expense Accounts** (optional): One or more account allocations with:
- Account: Searchable typeahead for expense accounts
- Location: Dropdown with "Shared" and client-specific locations
- Percentage: Numeric input (0-100), must total exactly 100% across all accounts
### 4. Account Location Validation
- **FR-4.1**: If an account has a fixed location configured, the selected location MUST match it
- **FR-4.2**: If an account has no fixed location, the selected location must be either "Shared" or one of the client's locations
- **FR-4.3**: Invalid locations must be rejected with a clear error message
### 5. Percentage Validation
- **FR-5.1**: When accounts are provided, the sum of all percentages must equal exactly 100%
- **FR-5.2**: Values must be between 0 and 100
- **FR-5.3**: Invalid totals must be rejected with a clear error message showing the actual total
### 6. Amount Distribution
- **FR-6.1**: Percentages are converted to dollar amounts per transaction based on each transaction's amount
- **FR-6.2**: For "Shared" location, amounts are distributed evenly across all client locations (with proper cent handling)
- **FR-6.3**: Rounding errors are absorbed by the last account row
- **FR-6.4**: Each transaction gets its own set of transaction-account entities
### 7. Submission Behavior
- **FR-7.1**: Submitting with no accounts, no vendor, and no status should be a no-op (or rejected)
- **FR-7.2**: On success, all selected non-locked transactions are updated
- **FR-7.3**: Success response triggers a table refresh
- **FR-7.4**: Modal closes on success
## UI/UX Requirements (from Master)
### SSR-Specific Adaptations
- The SSR version uses a modal wizard with HTMX instead of a re-frame modal
- Form state is managed server-side via `multi-form-state`
- Percentage inputs display as whole numbers (50 for 50%) but are stored as decimals (0.5)
## Test Scenarios
### Happy Path
1. Select single transaction, code with 100% to one account
2. Select multiple transactions, code with vendor + status + accounts
3. Select all visible transactions via header checkbox
### Validation
4. Submit without any changes (no vendor, no status, no accounts)
5. Submit with accounts totaling < 100%
6. Submit with accounts totaling > 100%
7. Submit with invalid location for account
8. Submit with location not belonging to client
### Edge Cases
9. All selected transactions are locked (count should be 0)
10. Mix of locked and unlocked transactions (only unlocked should be coded)
11. "Shared" location distributes across multiple client locations
## Known Issues to Verify
1. **Missing location validation**: The SSR version (`bulk_code.clj`) does not validate account locations against client locations or account fixed locations (present in GraphQL version)
2. **Approval status options**: Verify "excluded" vs "suppressed" naming consistency

View File

@@ -0,0 +1,493 @@
import { test, expect } from '@playwright/test';
let testInfoCache: any = null;
async function getTestInfo(page: any) {
const response = await page.request.get('/test-info');
testInfoCache = await response.json();
return testInfoCache;
}
async function navigateToTransactions(page: any, clientMode: string = 'mine') {
await page.setExtraHTTPHeaders({
'x-clients': clientMode === 'all' ? '"all"' : '"mine"'
});
await page.goto('/transaction2');
await page.waitForSelector('table tbody tr');
}
async function selectTransactionByIndex(page: any, index: number) {
const rows = page.locator('table tbody tr');
const row = rows.nth(index);
const checkbox = row.locator('input[type="checkbox"][name="id"]').first();
await checkbox.click();
await page.waitForTimeout(200);
}
async function selectAllTransactions(page: any) {
const headerCheckbox = page.locator('input#checkbox-all').first();
await headerCheckbox.click();
await page.waitForTimeout(200);
}
async function openBulkCodeModal(page: any) {
const codeButton = page.locator('button:has-text("Code")').first();
await codeButton.click();
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
await page.waitForSelector('#wizardmodal');
}
async function closeBulkCodeModal(page: any) {
// The success response swaps the modal content, but the modal holder stays open
// Wait for the success message to appear
await page.waitForSelector('text=Successfully coded', { timeout: 10000 });
}
async function selectAccountFromTypeahead(page: any, rowIndex: number, accountKey: string) {
const testInfo = await getTestInfo(page);
const accountId = testInfo.accounts[accountKey];
if (!accountId) {
throw new Error(`Could not find account with key ${accountKey}`);
}
const allRows = page.locator('#account-entries tbody tr');
const rowCount = await allRows.count();
let accountRow = null;
let accountRowIndex = 0;
for (let i = 0; i < rowCount; i++) {
const row = allRows.nth(i);
const hasAccountInput = await row.locator('input[name*="account"]').count() > 0;
if (hasAccountInput) {
if (accountRowIndex === rowIndex) {
accountRow = row;
break;
}
accountRowIndex++;
}
}
if (!accountRow) {
throw new Error(`Could not find account row at index ${rowIndex}`);
}
const hiddenInput = accountRow.locator('input[type="hidden"][name*="[account]"]').first();
await hiddenInput.evaluate((el: HTMLInputElement, value: string) => {
// Replace the Alpine-managed hidden input with a plain one
const newInput = document.createElement('input');
newInput.type = 'hidden';
newInput.name = el.name;
newInput.value = value;
el.parentNode.replaceChild(newInput, el);
}, accountId.toString());
await page.waitForTimeout(300);
// Trigger the location select reload by dispatching 'changed' event
const locationContainer = accountRow.locator('[x-dispatch\\:changed]').first();
if (await locationContainer.count() > 0) {
await locationContainer.evaluate((el: HTMLElement) => {
el.dispatchEvent(new CustomEvent('changed', { bubbles: true }));
});
await page.waitForTimeout(500);
}
}
async function findAccountRow(page: any, rowIndex: number) {
const allRows = page.locator('#account-entries tbody tr');
const rowCount = await allRows.count();
let accountRowIndex = 0;
for (let i = 0; i < rowCount; i++) {
const row = allRows.nth(i);
const hasAccountInput = await row.locator('input[name*="account"]').count() > 0;
if (hasAccountInput) {
if (accountRowIndex === rowIndex) {
return row;
}
accountRowIndex++;
}
}
throw new Error(`Could not find account row at index ${rowIndex}`);
}
async function setAccountPercentage(page: any, rowIndex: number, percentage: string) {
const row = await findAccountRow(page, rowIndex);
const percentageInput = row.locator('input[name*="percentage"]').first();
await percentageInput.fill(percentage);
await percentageInput.dispatchEvent('change');
await page.waitForTimeout(300);
}
async function setAccountLocation(page: any, rowIndex: number, location: string) {
const row = await findAccountRow(page, rowIndex);
const locationSelect = row.locator('select[name*="location"]').first();
// If the option doesn't exist, add it (for testing invalid locations)
const optionExists = await locationSelect.locator(`option[value="${location}"]`).count() > 0;
if (!optionExists) {
await locationSelect.evaluate((el: HTMLSelectElement, value: string) => {
const option = document.createElement('option');
option.value = value;
option.textContent = value;
el.appendChild(option);
}, location);
}
await locationSelect.selectOption(location);
await locationSelect.dispatchEvent('change');
await page.waitForTimeout(300);
}
async function addNewAccount(page: any) {
const newAccountButton = page.locator('a:has-text("New account")').first();
await newAccountButton.click();
await page.waitForTimeout(500);
}
async function submitBulkCodeForm(page: any) {
const form = page.locator('#wizard-form');
await form.evaluate((el: HTMLFormElement) => {
el.dispatchEvent(new Event('submit', { bubbles: true }));
});
}
async function getModalErrorText(page: any) {
const errorElement = page.locator('#form-errors .error-content');
try {
await errorElement.waitFor({ state: 'visible', timeout: 3000 });
return await errorElement.textContent();
} catch {
return null;
}
}
test.describe.configure({ mode: 'serial' });
test.describe('Bulk Code Transactions - Happy Path', () => {
test('should bulk code a single transaction with vendor, status, and 100% account', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
// Verify modal shows correct count
await expect(page.locator('text=Bulk editing 1 transactions')).toBeVisible();
// Select vendor
const vendorHidden = page.locator('input[type="hidden"][name="step-params[vendor]"]').first();
const testInfo = await getTestInfo(page);
await vendorHidden.evaluate((el: HTMLInputElement, value: string) => {
const newInput = document.createElement('input');
newInput.type = 'hidden';
newInput.name = el.name;
newInput.value = value;
el.parentNode.replaceChild(newInput, el);
}, testInfo.accounts.vendor.toString());
await page.waitForTimeout(300);
// Select approval status
const statusSelect = page.locator('select[name="step-params[approval-status]"]').first();
await statusSelect.selectOption('approved');
// Add account
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '100');
// Submit
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
// Verify success by checking table refreshed
await page.waitForSelector('table tbody tr');
});
test('should bulk code multiple selected transactions', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await selectTransactionByIndex(page, 1);
await openBulkCodeModal(page);
// Should show count of selected transactions
await expect(page.locator('text=Bulk editing 2 transactions')).toBeVisible();
// Add account at 100%
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '100');
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
await page.waitForSelector('table tbody tr');
});
test('should bulk code all visible transactions via header checkbox', async ({ page }) => {
await navigateToTransactions(page);
await selectAllTransactions(page);
await openBulkCodeModal(page);
// Should show all transactions
await expect(page.locator('text=Bulk editing 3 transactions')).toBeVisible();
// Add account at 100%
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '100');
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
await page.waitForSelector('table tbody tr');
});
});
test.describe('Bulk Code Transactions - Validation', () => {
test('should reject when no vendor, status, or accounts provided', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
// Submit without any changes
await submitBulkCodeForm(page);
await page.waitForTimeout(1000);
// Modal should still be open
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
});
test('should reject when account percentages total less than 100%', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '50');
await submitBulkCodeForm(page);
await page.waitForTimeout(1000);
// Modal should still be open
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
// Should show validation error
const errorText = await getModalErrorText(page);
expect(errorText).toContain('does not equal 100%');
});
test('should reject when account percentages total more than 100%', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '150');
await submitBulkCodeForm(page);
await page.waitForTimeout(1000);
// Modal should still be open
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
});
test('should reject invalid location for account with fixed location', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
await addNewAccount(page);
// Use the fixed-location account
await selectAccountFromTypeahead(page, 0, 'fixed-location-account');
// Try to set wrong location (account is fixed to "DT", try "INVALID")
await setAccountLocation(page, 0, 'INVALID');
await setAccountPercentage(page, 0, '100');
await submitBulkCodeForm(page);
await page.waitForTimeout(1000);
// Modal should still be open
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
// Should show validation error about location mismatch
const errorText = await getModalErrorText(page);
expect(errorText).toContain('location');
});
test('should reject location not belonging to client', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
// Client only has "DT" location, try "GR"
await setAccountLocation(page, 0, 'GR');
await setAccountPercentage(page, 0, '100');
await submitBulkCodeForm(page);
await page.waitForTimeout(1000);
// Modal should still be open
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
// Should show validation error
const errorText = await getModalErrorText(page);
expect(errorText).toContain('location');
});
});
test.describe('Bulk Code Transactions - Account Distribution', () => {
test('should split 50/50 between two accounts', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
// First account at 50%
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'DT');
await setAccountPercentage(page, 0, '50');
// Second account at 50%
await addNewAccount(page);
await selectAccountFromTypeahead(page, 1, 'second-account');
await setAccountLocation(page, 1, 'DT');
await setAccountPercentage(page, 1, '50');
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
await page.waitForSelector('table tbody tr');
});
test('should allow Shared location for account without fixed location', async ({ page }) => {
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '100');
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
await page.waitForSelector('table tbody tr');
});
});
test.describe('Bulk Code Transactions - Vendor Pre-population', () => {
test('should pre-populate default account when vendor is selected', async ({ page }) => {
// Ensure single-client mode
await page.request.get('/test-set-client-mode?mode=single-client');
await navigateToTransactions(page);
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
// Select vendor (test vendor has default-account set to test-account)
const testInfo = await getTestInfo(page);
const vendorId = testInfo.accounts.vendor;
// The vendor typeahead dispatches change from its parent div
// We need to set the hidden input and dispatch change on the container
const vendorContainer = page.locator('div[hx-post*="vendor-changed"]').first();
const vendorHidden = vendorContainer.locator('input[type="hidden"]').first();
await vendorHidden.evaluate((el: HTMLInputElement, value: string) => {
const newInput = document.createElement('input');
newInput.type = 'hidden';
newInput.name = el.name;
newInput.value = value;
el.parentNode.replaceChild(newInput, el);
}, vendorId.toString());
// Dispatch change on the container to trigger HTMX
await vendorContainer.evaluate((el: HTMLElement) => {
el.dispatchEvent(new Event('change', { bubbles: true }));
});
// Wait for HTMX response
await page.waitForResponse(response => response.url().includes('/vendor-changed') && response.status() === 200);
await page.waitForTimeout(500);
// Account should be pre-populated - check for account row
const accountRows = page.locator('#account-entries tbody tr');
const rowCount = await accountRows.count();
// Should have at least 1 account row (the default account) plus the new-row button
expect(rowCount).toBeGreaterThanOrEqual(2);
// The account should have a hidden input with the test-account ID
const accountHidden = page.locator('input[type="hidden"][name*="[account]"]').first();
const accountValue = await accountHidden.inputValue();
expect(accountValue).toBe(testInfo.accounts['test-account'].toString());
// Percentage should be 100
const percentageInput = page.locator('input[name*="percentage"]').first();
const percentageValue = await percentageInput.inputValue();
expect(percentageValue).toBe('100');
// Submit should succeed
await submitBulkCodeForm(page);
await closeBulkCodeModal(page);
await page.waitForSelector('table tbody tr');
});
test('should NOT pre-populate default account when user has multiple clients', async ({ page }) => {
// Switch to multi-client mode
await page.request.get('/test-set-client-mode?mode=multi-client');
// Use 'all' to see all clients in the database
await navigateToTransactions(page, 'all');
await selectTransactionByIndex(page, 0);
await openBulkCodeModal(page);
// Select vendor
const testInfo = await getTestInfo(page);
const vendorId = testInfo.accounts.vendor;
const vendorContainer = page.locator('div[hx-post*="vendor-changed"]').first();
const vendorHidden = vendorContainer.locator('input[type="hidden"]').first();
await vendorHidden.evaluate((el: HTMLInputElement, value: string) => {
const newInput = document.createElement('input');
newInput.type = 'hidden';
newInput.name = el.name;
newInput.value = value;
el.parentNode.replaceChild(newInput, el);
}, vendorId.toString());
// Dispatch change on the container to trigger HTMX
await vendorContainer.evaluate((el: HTMLElement) => {
el.dispatchEvent(new Event('change', { bubbles: true }));
});
// Wait for HTMX response
await page.waitForResponse(response => response.url().includes('/vendor-changed') && response.status() === 200);
await page.waitForTimeout(500);
// Should NOT have pre-populated account rows - only the "New account" button row
const accountRows = page.locator('#account-entries tbody tr');
const rowCount = await accountRows.count();
// With multi-client, no pre-population should happen, so only 1 row (the "New account" button)
expect(rowCount).toBe(1);
// Switch back to single-client mode for other tests
await page.request.get('/test-set-client-mode?mode=single-client');
});
});

View File

@@ -0,0 +1,333 @@
import { test, expect } from '@playwright/test';
async function openEditModal(page: any) {
// Navigate to transactions page
await page.goto('/transaction2');
// Wait for the table to load
await page.waitForSelector('table tbody tr');
// Find and click the edit button for the test transaction
const editButton = page.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').first();
await editButton.click();
// Wait for the modal to open
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
await page.waitForSelector('#wizardmodal');
// Click Next to go to the links step (button says "Transaction Actions")
await page.click('button:has-text("Transaction Actions")');
// Wait for the links step to load
await page.waitForSelector('text=Transaction Actions', { state: 'visible' });
// Click on "Manual" tab
await page.click('button:has-text("Manual")');
// Wait for the manual form to appear
await page.waitForSelector('#account-grid-body');
}
let testInfoCache: any = null;
async function getTestInfo(page: any) {
// Always fetch fresh to handle server restarts
const response = await page.request.get('/test-info');
testInfoCache = await response.json();
return testInfoCache;
}
async function selectAccountFromTypeahead(page: any, rowIndex: number, accountName: string) {
// The account search uses Solr which isn't available in tests.
// Instead, we directly set the hidden input value via JavaScript.
// Get all rows except the new-row, total, balance, and transaction total rows
const allRows = page.locator('#account-grid-body tbody tr');
const rowCount = await allRows.count();
// Find the row that has a hidden input for account (actual account rows)
let accountRow = null;
let accountRowIndex = 0;
for (let i = 0; i < rowCount; i++) {
const row = allRows.nth(i);
const hasAccountInput = await row.locator('input[name*="transaction-account/account"]').count() > 0;
if (hasAccountInput) {
if (accountRowIndex === rowIndex) {
accountRow = row;
break;
}
accountRowIndex++;
}
}
if (!accountRow) {
throw new Error(`Could not find account row at index ${rowIndex}`);
}
// Find the hidden input for the account
const hiddenInput = accountRow.locator('input[type="hidden"][name*="transaction-account/account"]').first();
// Get account IDs from test-info endpoint
const testInfo = await getTestInfo(page);
const accountKey = accountName === 'Test' ? 'test-account' : 'second-account';
const accountId = testInfo.accounts[accountKey];
if (!accountId) {
throw new Error(`Could not find account with name ${accountName}`);
}
// Set the hidden input value and trigger change
// Also update Alpine.js data to prevent it from overwriting our value
await hiddenInput.evaluate((el: HTMLInputElement, value: string) => {
// Set the DOM value
el.value = value;
// Update Alpine.js component data
const alpineEl = el.closest('[x-data]');
if (alpineEl && (alpineEl as any).__x) {
(alpineEl as any).__x.$data.value.value = parseInt(value);
(alpineEl as any).__x.$data.value.label = 'Selected Account';
}
// Also update any parent Alpine model (accountId)
const rowEl = el.closest('tr[x-data]');
if (rowEl && (rowEl as any).__x) {
(rowEl as any).__x.$data.accountId = parseInt(value);
}
el.dispatchEvent(new Event('change', { bubbles: true }));
}, accountId.toString());
// Wait for any HTMX updates
await page.waitForTimeout(300);
}
async function findAccountRow(page: any, rowIndex: number) {
const allRows = page.locator('#account-grid-body tbody tr');
const rowCount = await allRows.count();
let accountRowIndex = 0;
for (let i = 0; i < rowCount; i++) {
const row = allRows.nth(i);
const hasAccountInput = await row.locator('input[name*="transaction-account/account"]').count() > 0;
if (hasAccountInput) {
if (accountRowIndex === rowIndex) {
return row;
}
accountRowIndex++;
}
}
throw new Error(`Could not find account row at index ${rowIndex}`);
}
async function setAccountAmount(page: any, rowIndex: number, amount: string) {
const row = await findAccountRow(page, rowIndex);
const amountInput = row.locator('input[name*="transaction-account/amount"]').first();
await amountInput.fill(amount);
await amountInput.dispatchEvent('change');
await page.waitForTimeout(300);
}
async function addNewAccount(page: any) {
// Click the "New account" button
await page.click('text=New account');
// Wait for the new row to be added
await page.waitForTimeout(500);
}
async function saveTransaction(page: any) {
// Submit the form directly instead of clicking the button
// The Done button might not have type="submit"
await page.evaluate(() => {
const form = document.querySelector('#wizard-form') as HTMLFormElement;
if (form) {
form.dispatchEvent(new Event('submit', { bubbles: true }));
}
});
// Wait for the modal to close
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'hidden', timeout: 10000 });
}
async function toggleToPercentMode(page: any) {
const percentRadio = page.locator('input[name="step-params[amount-mode]"][value="%"]');
await percentRadio.click();
// Wait for HTMX to swap the grid body
await page.waitForResponse(response =>
response.url().includes('/toggle-amount-mode') && response.status() === 200
);
await page.waitForTimeout(200);
}
async function toggleToDollarMode(page: any) {
const dollarRadio = page.locator('input[name="step-params[amount-mode]"][value="$"]');
await dollarRadio.click();
// Wait for HTMX to swap the grid body
await page.waitForResponse(response =>
response.url().includes('/toggle-amount-mode') && response.status() === 200
);
await page.waitForTimeout(200);
}
test.describe.configure({ mode: 'serial' });
test.describe('Transaction Edit Full Workflow', () => {
test('should code transaction with vendor using percentage, then split 50/50, then switch to dollars', async ({ page }) => {
// Step 1: Open edit modal and code with 100% to one account
await openEditModal(page);
// Switch to percentage mode
await toggleToPercentMode(page);
// Add a new account row
await addNewAccount(page);
// Select the account
await selectAccountFromTypeahead(page, 0, 'Test');
// Set amount to 100%
await setAccountAmount(page, 0, '100');
// Save the transaction
await saveTransaction(page);
// Step 2: Re-open and split 50/50 with two accounts
await openEditModal(page);
// Note: amount-mode is UI-only state, so it resets to $ when re-opening
// Switch back to percentage mode
await toggleToPercentMode(page);
// The existing account from step 1 should already be there
// Change its amount from 100% to 50%
await setAccountAmount(page, 0, '50');
// Add a second account at 50%
await addNewAccount(page);
await selectAccountFromTypeahead(page, 1, 'Second');
await setAccountAmount(page, 1, '50');
// Save
await saveTransaction(page);
// Step 3: Re-open and verify dollar amounts
await openEditModal(page);
// The accounts should be persisted from the previous save
// Wait for accounts to load
await page.waitForTimeout(500);
// Verify we're in dollar mode (default)
const dollarRadio = page.locator('input[name="step-params[amount-mode]"][value="$"]');
await expect(dollarRadio).toBeChecked();
// Verify amounts are in dollars (converted from percentages on save)
const row0 = await findAccountRow(page, 0);
const row1 = await findAccountRow(page, 1);
const amount0 = row0.locator('input[name*="transaction-account/amount"]').first();
const amount1 = row1.locator('input[name*="transaction-account/amount"]').first();
// Each should be $50.00 (or close to it)
const val0 = await amount0.inputValue();
const val1 = await amount1.inputValue();
expect(parseFloat(val0)).toBeCloseTo(50.0, 1);
expect(parseFloat(val1)).toBeCloseTo(50.0, 1);
// Save
await saveTransaction(page);
});
});
test.describe('Transaction Edit Validation', () => {
test('should show validation error when account totals do not match transaction amount', async ({ page }) => {
await openEditModal(page);
// Stay in dollar mode (default)
// Add an account
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'Test');
// Set amount to $50 (which doesn't match the $100 transaction)
await setAccountAmount(page, 0, '50');
// Try to save - this should fail because $50 != $100
// We submit the form and expect the modal to stay open
await page.evaluate(() => {
const form = document.querySelector('#wizard-form') as HTMLFormElement;
if (form) {
form.dispatchEvent(new Event('submit', { bubbles: true }));
}
});
// Wait a bit for the response
await page.waitForTimeout(1000);
// Modal should still be open (save failed)
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
// The form should still be present
const form = page.locator('#wizard-form');
await expect(form).toBeVisible();
// Verify the account row is still there with our $50 value
const amountInput = page.locator('input[name*="transaction-account/amount"]').first();
const value = await amountInput.inputValue();
expect(parseFloat(value)).toBeCloseTo(50.0, 1);
});
});
async function openEditModalForTransaction(page: any, description: string) {
// Navigate to transactions page
await page.goto('/transaction2');
// Wait for the table to load
await page.waitForSelector('table tbody tr');
// Find the row with the specific description and click its edit button
const row = page.locator('table tbody tr', { hasText: description }).first();
const editButton = row.locator('button[hx-get*="/transaction2/"][hx-get*="/edit"]').first();
await editButton.click();
// Wait for the modal to open
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
await page.waitForSelector('#wizardmodal');
// Click Next to go to the links step (button says "Transaction Actions")
await page.click('button:has-text("Transaction Actions")');
// Wait for the links step to load
await page.waitForSelector('text=Transaction Actions', { state: 'visible' });
}
test.describe('Transaction Link Date Display', () => {
test('should show payment date when linking to payment', async ({ page }) => {
await openEditModalForTransaction(page, 'Transaction for payment link');
// Click on "Link to payment" tab
await page.click('button:has-text("Link to payment")');
await page.waitForTimeout(500);
// Verify the payment option shows the date
await expect(page.locator('#payment-matches')).toContainText('Available Payments');
await expect(page.locator('#payment-matches')).toContainText('06/14/2023');
});
test('should show invoice date when linking to unpaid invoice', async ({ page }) => {
await openEditModalForTransaction(page, 'Transaction for unpaid invoice link');
// Click on "Link to unpaid invoices" tab
await page.click('button:has-text("Link to unpaid invoices")');
await page.waitForTimeout(500);
// Verify the invoice option shows the date
await expect(page.locator('text=Available Unpaid Invoices')).toBeVisible();
await expect(page.locator('text=UNPAID-001')).toBeVisible();
await expect(page.locator('text=07/19/2023')).toBeVisible();
});
});

View File

@@ -14,7 +14,7 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn dollars= [amt1 amt2] (defn dollars= [amt1 amt2]
(dollars-0? (- amt1 amt2) )) (dollars-0? (- amt1 amt2)))
(defn localize [d] (defn localize [d]
(time/to-time-zone d (time/time-zone-for-id "America/Los_Angeles"))) (time/to-time-zone d (time/time-zone-for-id "America/Los_Angeles")))
@@ -22,7 +22,6 @@
(defn local-now [] (defn local-now []
(localize (time/now))) (localize (time/now)))
(defn recent-date (defn recent-date
([] ([]
(recent-date 90)) (recent-date 90))
@@ -34,14 +33,14 @@
(->> d (->> d
(coerce/to-date-time) (coerce/to-date-time)
localize localize
(f/unparse excel-formatter ))) (f/unparse excel-formatter)))
(def iso-formatter (f/with-zone (f/formatter "yyyy-MM-dd") (time/time-zone-for-id "America/Los_Angeles"))) (def iso-formatter (f/with-zone (f/formatter "yyyy-MM-dd") (time/time-zone-for-id "America/Los_Angeles")))
(defn iso-date [d] (defn iso-date [d]
(->> d (->> d
(coerce/to-date-time) (coerce/to-date-time)
localize localize
(f/unparse iso-formatter ))) (f/unparse iso-formatter)))
(defn sales-orders-in-range [db client start end] (defn sales-orders-in-range [db client start end]
(let [end (or end #inst "2050-01-01")] (let [end (or end #inst "2050-01-01")]
@@ -53,9 +52,6 @@
[client start] [client start]
[client end])))) [client end]))))
(defn can-see-client? [identity client] (defn can-see-client? [identity client]
(when (not client) (when (not client)
(println "WARNING - permission checking for null client")) (println "WARNING - permission checking for null client"))
@@ -63,11 +59,9 @@
((set (map :db/id (:user/clients identity))) (:db/id client)) ((set (map :db/id (:user/clients identity))) (:db/id client))
((set (map :db/id (:user/clients identity))) client))) ((set (map :db/id (:user/clients identity))) client)))
(defn ->pattern [x] (defn ->pattern [x]
(. java.util.regex.Pattern (compile x java.util.regex.Pattern/CASE_INSENSITIVE))) (. java.util.regex.Pattern (compile x java.util.regex.Pattern/CASE_INSENSITIVE)))
(defn dom [^java.util.Date x] (defn dom [^java.util.Date x]
(-> x (-> x
(.toInstant) (.toInstant)
@@ -85,8 +79,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:sales-order/client+date :sales-order/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-charges [db clients start end] (defn scan-charges [db clients start end]
@@ -94,8 +88,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:charge/client+date :charge/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-sales-refunds [db clients start end] (defn scan-sales-refunds [db clients start end]
@@ -103,8 +97,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:sales-refund/client+date :sales-refund/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-expected-deposits [db clients start end] (defn scan-expected-deposits [db clients start end]
@@ -112,8 +106,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:expected-deposit/client+date :expected-deposit/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-cash-drawer-shifts [db clients start end] (defn scan-cash-drawer-shifts [db clients start end]
@@ -121,8 +115,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:cash-drawer-shift/client+date :cash-drawer-shift/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-invoices [db clients start end] (defn scan-invoices [db clients start end]
@@ -130,8 +124,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:invoice/client+date :invoice/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-transactions [db clients start end] (defn scan-transactions [db clients start end]
@@ -139,8 +133,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:transaction/client+date :transaction/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-ledger [db clients start end] (defn scan-ledger [db clients start end]
@@ -148,8 +142,8 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:journal-entry/client+date :journal-entry/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn scan-payments [db clients start end] (defn scan-payments [db clients start end]
@@ -157,15 +151,14 @@
:let [c (entid db c)] :let [c (entid db c)]
r (seq (dc/index-range db r (seq (dc/index-range db
:payment/client+date :payment/client+date
[c (or start #inst "2001-01-01T08:00:00.000-00:00") ] [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
[c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))] [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))])) [(:e r) (first (:v r)) (second (:v r))]))
(defn ident [x] (defn ident [x]
(:db/ident x)) (:db/ident x))
(deftype Line [^Long id ^Long client-id ^Long account-id ^String location ^java.util.Date date ^Double debit ^Double credit ^Double running-balance] (deftype Line [^Long id ^Long client-id ^Long account-id ^String location ^java.util.Date date ^Double debit ^Double credit ^Double running-balance])
)
(defmethod print-method Line [entity writer] (defmethod print-method Line [entity writer]
(.write writer (format "Line %d: client:%d account:%d location:%s date:%s" (.write writer (format "Line %d: client:%d account:%d location:%s date:%s"
@@ -175,12 +168,10 @@
(.-location entity) (.-location entity)
(iso-date (.-date entity))))) (iso-date (.-date entity)))))
(defn ->line [{[current-client current-account current-location current-date debit credit running-balance] (defn ->line [{[current-client current-account current-location current-date debit credit running-balance]
:v :v
id :e}] id :e}]
(Line. id current-client current-account current-location current-date debit credit running-balance) (Line. id current-client current-account current-location current-date debit credit running-balance))
)
(defn compare-account [^Line l1 ^Line l2] (defn compare-account [^Line l1 ^Line l2]
@@ -194,7 +185,7 @@
(seq) (seq)
(map ->line) (map ->line)
(partition-by (fn set-partition [^Line l] (partition-by (fn set-partition [^Line l]
[(.-account-id l) (.-location l)]))) ] [(.-account-id l) (.-location l)])))]
(->> running-balance-set (->> running-balance-set
(sort compare-account)))) (sort compare-account))))
@@ -205,11 +196,11 @@
(take-while (fn until-date [^Line l] (take-while (fn until-date [^Line l]
(let [^java.util.Date d (.-date l)] (let [^java.util.Date d (.-date l)]
(<= (.compareTo ^java.util.Date d end) 0)))) (<= (.compareTo ^java.util.Date d end) 0))))
last) ] last)]
:when (and z (.-id z))] :when (and z (.-id z))]
[(.-client-id z) (.-account-id z) (.-location z) (.-date z) (.-running-balance z)])) [(.-client-id z) (.-account-id z) (.-location z) (.-date z) (.-running-balance z)]))
#_(doseq [[ n] (dc/q '[:find ?cd :where [?c :client/code ?cd] [?c :client/groups "NTG"]] (dc/db auto-ap.datomic/conn))] #_(doseq [[n] (dc/q '[:find ?cd :where [?c :client/code ?cd] [?c :client/groups "NTG"]] (dc/db auto-ap.datomic/conn))]
(println n) (println n)
(dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance
:in $ ?end ?group :in $ ?end ?group
@@ -269,7 +260,6 @@
(do (do
[client-id account-id location debits credits current-balance count sample])))) [client-id account-id location debits credits current-balance count sample]))))
(comment (comment
(->> (->>
(detailed-account-snapshot (dc/db auto-ap.datomic/conn) (detailed-account-snapshot (dc/db auto-ap.datomic/conn)
@@ -280,12 +270,12 @@
(into #{}) (into #{})
seq) seq)
(account-snapshot (dc/db auto-ap.datomic/conn) (account-snapshot (dc/db auto-ap.datomic/conn)
(auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn) (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
[:client/code "NGOP"]) [:client/code "NGOP"])
#inst "2022-01-01") #inst "2022-01-01")
(def orig (->> [:client/code "NGOP"] (def orig (->> [:client/code "NGOP"]
(auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)) (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn))
(account-sets (dc/db auto-ap.datomic/conn)) (account-sets (dc/db auto-ap.datomic/conn))
(mapcat (fn [ls] (mapcat (fn [ls]
@@ -293,9 +283,9 @@
(filter (fn [l] (nil? (.-location l)))) (filter (fn [l] (nil? (.-location l))))
(into #{}))) (into #{})))
(.-location orig) (.-location orig)
(def orig (into [] (take 5000 (mapcat (fn [ls] (def orig (into [] (take 5000 (mapcat (fn [ls]
(map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn) (map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn)
(auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn) (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
[:client/code "NGOP"])))))) [:client/code "NGOP"]))))))
@@ -307,14 +297,14 @@
(= orig n) (= orig n)
#_(seq (dc/q '[:find ?c ?a ?l ?date ?balance #_(seq (dc/q '[:find ?c ?a ?l ?date ?balance
:in $ :in $
:where [?c :client/code "NGOP"] :where [?c :client/code "NGOP"]
[(iol-ion.query/account-snapshot $ ?c #inst "2023-01-01") [?x ...]] [(iol-ion.query/account-snapshot $ ?c #inst "2023-01-01") [?x ...]]
[(untuple ?x) [_ ?a ?l ?date ?balance]]] [(untuple ?x) [_ ?a ?l ?date ?balance]]]
(dc/db auto-ap.datomic/conn))) (dc/db auto-ap.datomic/conn)))
#_(->> (seq (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance ?end4 #_(->> (seq (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance ?end4
:in $ ?end ?group :in $ ?end ?group
:where :where
[(clj-time.coerce/to-date-time ?end) ?end2] [(clj-time.coerce/to-date-time ?end) ?end2]

View File

@@ -11,7 +11,6 @@
(def pull-many iol-ion.utils/pull-many) (def pull-many iol-ion.utils/pull-many)
(def remove-nils iol-ion.utils/remove-nils) (def remove-nils iol-ion.utils/remove-nils)
;; TODO expected-deposit ledger entry ;; TODO expected-deposit ledger entry
#_(defmethod entity-change->ledger :expected-deposit #_(defmethod entity-change->ledger :expected-deposit
[db [type id]] [db [type id]]
@@ -33,9 +32,6 @@
:location "A" :location "A"
:account :account/ccp}]})) :account :account/ccp}]}))
(defn regenerate-literals [] (defn regenerate-literals []
(require 'com.github.ivarref.gen-fn) (require 'com.github.ivarref.gen-fn)
(spit (spit

View File

@@ -13,7 +13,7 @@
(:invoice/invoice-number invoice) (:invoice/invoice-number invoice)
(:invoice/client invoice) (:invoice/client invoice)
(:invoice/vendor invoice)))) (:invoice/vendor invoice))))
[ locked-until] (first (dc/q '[:find ?locked-until [locked-until] (first (dc/q '[:find ?locked-until
:in $ ?c :in $ ?c
:where [?c :client/locked-until ?locked-until]] :where [?c :client/locked-until ?locked-until]]
db db

View File

@@ -16,6 +16,6 @@
(-> [] (-> []
(into (map (fn [i] (if is-component? (into (map (fn [i] (if is-component?
[:db/retractEntity i] [:db/retractEntity i]
[:db/retract e a i ])) retract-ids)) [:db/retract e a i])) retract-ids))
(into (map (fn [i] [:db/add e a i]) new-rels)) (into (map (fn [i] [:db/add e a i]) new-rels))
(into (map (fn [i] [:upsert-entity i]) vs))))) (into (map (fn [i] [:upsert-entity i]) vs)))))

View File

@@ -12,5 +12,5 @@
retracts (filter (complement (set vs)) extant) retracts (filter (complement (set vs)) extant)
new (filter (complement (set extant)) vs)] new (filter (complement (set extant)) vs)]
(-> [] (-> []
(into (map (fn [i] [:db/retract e a i ]) retracts)) (into (map (fn [i] [:db/retract e a i]) retracts))
(into (map (fn [i] [:db/add e a i]) new))))) (into (map (fn [i] [:db/add e a i]) new)))))

View File

@@ -5,7 +5,6 @@
) )
(:import [java.util UUID])) (:import [java.util UUID]))
(defn -random-tempid [] (defn -random-tempid []
(str (UUID/randomUUID))) (str (UUID/randomUUID)))
@@ -36,7 +35,6 @@
;; :else ;; :else
;; v)) ;; v))
(defn upsert-entity [db entity] (defn upsert-entity [db entity]
(when-not (or (:db/id entity) (when-not (or (:db/id entity)
(:db/ident entity)) (:db/ident entity))

View File

@@ -7,8 +7,7 @@
(fn [m k v] (fn [m k v]
(if (not (nil? v)) (if (not (nil? v))
(assoc m k v) (assoc m k v)
m m))
))
{} {}
m)] m)]
(if (seq result) (if (seq result)
@@ -49,25 +48,22 @@
:journal-entry/line-items (into [(cond-> {:db/id (str raw-invoice-id "-" 0) :journal-entry/line-items (into [(cond-> {:db/id (str raw-invoice-id "-" 0)
:journal-entry-line/account :account/accounts-payable :journal-entry-line/account :account/accounts-payable
:journal-entry-line/location "A" :journal-entry-line/location "A"}
}
credit-invoice? (assoc :journal-entry-line/debit (Math/abs (:invoice/total entity))) credit-invoice? (assoc :journal-entry-line/debit (Math/abs (:invoice/total entity)))
(not credit-invoice?) (assoc :journal-entry-line/credit (Math/abs (:invoice/total entity))))] (not credit-invoice?) (assoc :journal-entry-line/credit (Math/abs (:invoice/total entity))))]
(map-indexed (fn [i ea] (map-indexed (fn [i ea]
(cond-> (cond->
{:db/id (str raw-invoice-id "-" (inc i)) {:db/id (str raw-invoice-id "-" (inc i))
:journal-entry-line/account (:db/id (:invoice-expense-account/account ea)) :journal-entry-line/account (:db/id (:invoice-expense-account/account ea))
:journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ") :journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ")}
}
credit-invoice? (assoc :journal-entry-line/credit (Math/abs (:invoice-expense-account/amount ea))) credit-invoice? (assoc :journal-entry-line/credit (Math/abs (:invoice-expense-account/amount ea)))
(not credit-invoice?) (assoc :journal-entry-line/debit (Math/abs (:invoice-expense-account/amount ea))))) (not credit-invoice?) (assoc :journal-entry-line/debit (Math/abs (:invoice-expense-account/amount ea)))))
(:invoice/expense-accounts entity))) (:invoice/expense-accounts entity)))
:journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01) :journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01)
(every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity)) (every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity)))}))))
)}))))
(defn current-date [db] (defn current-date [db]
(let [ last-tx (dc/t->tx (dc/basis-t db)) (let [last-tx (dc/t->tx (dc/basis-t db))
[[date]] (seq (dc/q '[:find ?ti :in $ ?tx [[date]] (seq (dc/q '[:find ?ti :in $ ?tx
:where [?tx :db/txInstant ?ti]] :where [?tx :db/txInstant ?ti]]
db db

View File

@@ -24,9 +24,8 @@
(def extant-read '[:db/id :journal-entry/date :journal-entry/client {:journal-entry/line-items [:journal-entry-line/account :journal-entry-line/location :db/id :journal-entry-line/client+account+location+date]}]) (def extant-read '[:db/id :journal-entry/date :journal-entry/client {:journal-entry/line-items [:journal-entry-line/account :journal-entry-line/location :db/id :journal-entry-line/client+account+location+date]}])
(defn current-date [db] (defn current-date [db]
(let [ last-tx (dc/t->tx (dc/basis-t db)) (let [last-tx (dc/t->tx (dc/basis-t db))
[[date]] (seq (dc/q '[:find ?ti :in $ ?tx [[date]] (seq (dc/q '[:find ?ti :in $ ?tx
:where [?tx :db/txInstant ?ti]] :where [?tx :db/txInstant ?ti]]
db db
@@ -51,7 +50,7 @@
(let [extant-entry (or (when-let [original-entity (:journal-entry/original-entity ledger-entry)] (let [extant-entry (or (when-let [original-entity (:journal-entry/original-entity ledger-entry)]
(dc/pull db extant-read [:journal-entry/original-entity original-entity])) (dc/pull db extant-read [:journal-entry/original-entity original-entity]))
(when-let [external-id (:journal-entry/external-id ledger-entry)] (when-let [external-id (:journal-entry/external-id ledger-entry)]
(dc/pull db extant-read [:journal-entry/external-id external-id]))) ] (dc/pull db extant-read [:journal-entry/external-id external-id])))]
(cond-> (cond->
[[:upsert-entity (into (-> ledger-entry [[:upsert-entity (into (-> ledger-entry

View File

@@ -30,8 +30,7 @@
total-debits (reduce + 0.0 (map #(get % :ledger-side/debit 0.0) aggregated)) total-debits (reduce + 0.0 (map #(get % :ledger-side/debit 0.0) aggregated))
total-credits (reduce + 0.0 (map #(get % :ledger-side/credit 0.0) aggregated)) total-credits (reduce + 0.0 (map #(get % :ledger-side/credit 0.0) aggregated))
_ (clojure.pprint/pprint [total-debits total-credits]) _ (clojure.pprint/pprint [total-debits total-credits])]
]
(when (and (seq line-items) (when (and (seq line-items)
(= (Math/round (* 1000 total-debits)) (= (Math/round (* 1000 total-debits))
(Math/round (* 1000 total-credits)))) (Math/round (* 1000 total-credits))))
@@ -65,6 +64,5 @@ _ (clojure.pprint/pprint [total-debits total-credits])
(concat (concat
[[:db/retractEntity [:journal-entry/original-entity (:db/id summary)]]] [[:db/retractEntity [:journal-entry/original-entity (:db/id summary)]]]
(when client-id [{:db/id client-id (when client-id [{:db/id client-id
:client/ledger-last-change (current-date db)}])))))) :client/ledger-last-change (current-date db)}]))))))

View File

@@ -81,7 +81,6 @@
[[:upsert-ledger journal-entry]] [[:upsert-ledger journal-entry]]
[[:db/retractEntity [:journal-entry/original-entity (:db/id transaction)]]])))) [[:db/retractEntity [:journal-entry/original-entity (:db/id transaction)]]]))))
#_(comment #_(comment
;; If transactions are failing, it is likely that there are multiple bank accounts linked ;; If transactions are failing, it is likely that there are multiple bank accounts linked
@@ -148,6 +147,4 @@
[:upsert-ledger my-journal]]) [:upsert-ledger my-journal]])
(auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681223) (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681223)
(auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681228) (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681228))
)

View File

@@ -14,7 +14,7 @@
{} {}
xs))) xs)))
(defn pull-many [db read ids ] (defn pull-many [db read ids]
(->> (dc/q '[:find (pull ?e r) (->> (dc/q '[:find (pull ?e r)
:in $ [?e ...] r] :in $ [?e ...] r]
db db
@@ -27,8 +27,7 @@
(fn [m k v] (fn [m k v]
(if (not (nil? v)) (if (not (nil? v))
(assoc m k v) (assoc m k v)
m m))
))
{} {}
m)] m)]
(if (seq result) (if (seq result)

98
package-lock.json generated
View File

@@ -26,6 +26,7 @@
"recharts": "^2.5.0" "recharts": "^2.5.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.60.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1" "webpack-cli": "^5.0.1"
@@ -189,6 +190,22 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@playwright/test": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
"integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@popperjs/core": { "node_modules/@popperjs/core": {
"version": "2.11.8", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1916,6 +1933,53 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/playwright": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.35", "version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
@@ -3335,6 +3399,15 @@
"optional": true, "optional": true,
"peer": true "peer": true
}, },
"@playwright/test": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
"integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
"dev": true,
"requires": {
"playwright": "1.60.0"
}
},
"@popperjs/core": { "@popperjs/core": {
"version": "2.11.8", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -4655,6 +4728,31 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"playwright": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
"playwright-core": "1.60.0"
},
"dependencies": {
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
}
}
},
"playwright-core": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
"dev": true
},
"postcss": { "postcss": {
"version": "8.4.35", "version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",

View File

@@ -24,12 +24,16 @@
"recharts": "^2.5.0" "recharts": "^2.5.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.60.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1" "webpack-cli": "^5.0.1"
}, },
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:server": "clojure -M:test -m auto-ap.test-server"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

26
playwright.config.ts Normal file
View File

@@ -0,0 +1,26 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3333',
trace: 'on-first-retry',
},
webServer: {
command: 'lein run -m auto-ap.test-server',
url: 'http://localhost:3333/test-info',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});

File diff suppressed because one or more lines are too long

11
skills-lock.json Normal file
View File

@@ -0,0 +1,11 @@
{
"version": 1,
"skills": {
"agent-browser": {
"source": "vercel-labs/agent-browser",
"sourceType": "github",
"skillPath": "skills/agent-browser/SKILL.md",
"computedHash": "228f87d57035100d9dc6efcfc05aafd4b6e3962adacaa04b8217ab2fadb15dc8"
}
}
}

View File

@@ -1,12 +1,11 @@
(ns amazonica.aws.textract (ns amazonica.aws.textract
(:require [amazonica.core :as amz]) (:require [amazonica.core :as amz])
(:import [com.amazonaws.services.textract AmazonTextractClient ])) (:import [com.amazonaws.services.textract AmazonTextractClient]))
#_ #_(import '[com.amazonaws.services.textract AmazonTextractClient])
(import '[com.amazonaws.services.textract AmazonTextractClient ]) #_(import '[com.amazonaws.services.textract.model S3Object])
#_(import '[com.amazonaws.services.textract.model S3Object ]) #_(import '[com.amazonaws.services.textract.model StartExpenseAnalysisRequest])
#_(import '[com.amazonaws.services.textract.model StartExpenseAnalysisRequest ]) #_(import '[com.amazonaws.services.textract.model GetExpenseAnalysisRequest])
#_(import '[com.amazonaws.services.textract.model GetExpenseAnalysisRequest ])
#_(import '[com.amazonaws.services.textract.model DocumentLocation]) #_(import '[com.amazonaws.services.textract.model DocumentLocation])
(amz/set-client AmazonTextractClient *ns*) (amz/set-client AmazonTextractClient *ns*)

View File

@@ -28,7 +28,6 @@
[(str "container:" (:DockerId container-data)) [(str "container:" (:DockerId container-data))
(str "ip:" (-> container-data :Networks first :IPv4Addresses first))]) (str "ip:" (-> container-data :Networks first :IPv4Addresses first))])
(mount/defstate container-tags (mount/defstate container-tags
:start (get-container-tags) :start (get-container-tags)
:stop nil) :stop nil)

View File

@@ -5,14 +5,11 @@
(path [cursor]) (path [cursor])
(state [cursor])) (state [cursor]))
(defprotocol ITransact (defprotocol ITransact
(-transact! [cursor f])) (-transact! [cursor f]))
(declare to-cursor cursor?) (declare to-cursor cursor?)
(deftype ValCursor [value state path] (deftype ValCursor [value state path]
IDeref IDeref
(deref [_] (deref [_]
@@ -26,7 +23,6 @@
(swap! state (if (empty? path) f #(update-in % path f))) (swap! state (if (empty? path) f #(update-in % path f)))
path))) path)))
(deftype MapCursor [value state path] (deftype MapCursor [value state path]
Counted Counted
(count [_] (count [_]
@@ -60,7 +56,6 @@
(for [[k v] @this] (for [[k v] @this]
[k (to-cursor v state (conj path k) nil)]))) [k (to-cursor v state (conj path k) nil)])))
(deftype VecCursor [value state path] (deftype VecCursor [value state path]
Counted Counted
(count [_] (count [_]
@@ -98,22 +93,18 @@
(for [[v i] (map vector @this (range))] (for [[v i] (map vector @this (range))]
(to-cursor v state (conj path i) nil)))) (to-cursor v state (conj path i) nil))))
(defn- to-cursor (defn- to-cursor
([v state path value] ([v state path value]
(cond (cond
(cursor? v) v (cursor? v) v
(map? v) (MapCursor. value state path) (map? v) (MapCursor. value state path)
(vector? v) (VecCursor. value state path) (vector? v) (VecCursor. value state path)
:else (ValCursor. value state path) :else (ValCursor. value state path))))
)))
(defn cursor? [c] (defn cursor? [c]
"Returns true if c is a cursor." "Returns true if c is a cursor."
(satisfies? ICursor c)) (satisfies? ICursor c))
(defn cursor [v] (defn cursor [v]
"Creates cursor from supplied value v. If v is an ordinary "Creates cursor from supplied value v. If v is an ordinary
data structure, it is wrapped into atom. If v is an atom, data structure, it is wrapped into atom. If v is an atom,
@@ -123,7 +114,6 @@
(if (instance? Atom v) v (atom v)) (if (instance? Atom v) v (atom v))
[] nil)) [] nil))
(defn synthetic-cursor [v prefix] (defn synthetic-cursor [v prefix]
(let [internal-cursor (cursor v)] (let [internal-cursor (cursor v)]
(reify ICursor (reify ICursor
@@ -132,14 +122,12 @@
(state [this] (state [this]
(state internal-cursor))))) (state internal-cursor)))))
(defn transact! [cursor f] (defn transact! [cursor f]
"Changes value beneath cursor by passing it to a single-argument "Changes value beneath cursor by passing it to a single-argument
function f. Old value will be passed as function argument. Function function f. Old value will be passed as function argument. Function
result will be the new value." result will be the new value."
(-transact! cursor f)) (-transact! cursor f))
(defn update! [cursor v] (defn update! [cursor v]
"Replaces value supplied by cursor with value v." "Replaces value supplied by cursor with value v."
(-transact! cursor (constantly v))) (-transact! cursor (constantly v)))

View File

@@ -49,8 +49,7 @@
(fn [m k v] (fn [m k v]
(if (not (nil? v)) (if (not (nil? v))
(assoc m k v) (assoc m k v)
m m))
))
{} {}
m)] m)]
(if (seq result) (if (seq result)
@@ -108,8 +107,7 @@
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/isComponent true :db/isComponent true
:db.install/_attribute :db.part/db :db.install/_attribute :db.part/db
:db/doc "The vendor's address"} :db/doc "The vendor's address"}])
])
(def client-schema (def client-schema
[{:db/ident :client/original-id [{:db/ident :client/original-id
@@ -151,8 +149,7 @@
:db/doc "Bank accounts for the client"}]) :db/doc "Bank accounts for the client"}])
(def address-schema (def address-schema
[ [{:db/ident :address/street1
{:db/ident :address/street1
:db/valueType :db.type/string :db/valueType :db.type/string
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "123 main st"} :db/doc "123 main st"}
@@ -174,8 +171,7 @@
:db/doc "95014"}]) :db/doc "95014"}])
(def contact-schema (def contact-schema
[ [{:db/ident :contact/name
{:db/ident :contact/name
:db/valueType :db.type/string :db/valueType :db.type/string
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "John Smith"} :db/doc "John Smith"}
@@ -188,8 +184,6 @@
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "hello@example.com"}]) :db/doc "hello@example.com"}])
(def bank-account-schema (def bank-account-schema
[{:db/ident :bank-account/external-id [{:db/ident :bank-account/external-id
:db/valueType :db.type/long :db/valueType :db.type/long
@@ -297,7 +291,6 @@
:db/isComponent true :db/isComponent true
:db/doc "The expense account categories for this invoice"} :db/doc "The expense account categories for this invoice"}
{:db/ident :invoice-status/paid} {:db/ident :invoice-status/paid}
{:db/ident :invoice-status/unpaid} {:db/ident :invoice-status/unpaid}
{:db/ident :invoice-status/voided}]) {:db/ident :invoice-status/voided}])
@@ -321,8 +314,6 @@
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "The amount that this contributes to"}]) :db/doc "The amount that this contributes to"}])
(def payment-schema (def payment-schema
[{:db/ident :payment/original-id [{:db/ident :payment/original-id
:db/valueType :db.type/long :db/valueType :db.type/long
@@ -374,8 +365,7 @@
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "raw data used to generate check pdf"} :db/doc "raw data used to generate check pdf"}
;; relations
;; relations
{:db/ident :payment/vendor {:db/ident :payment/vendor
:db/valueType :db.type/ref :db/valueType :db.type/ref
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
@@ -400,8 +390,7 @@
{:db/ident :payment-type/cash} {:db/ident :payment-type/cash}
{:db/ident :payment-type/check} {:db/ident :payment-type/check}
{:db/ident :payment-type/debit} {:db/ident :payment-type/debit}])
])
(def invoice-payment-schema (def invoice-payment-schema
[{:db/ident :invoice-payment/original-id [{:db/ident :invoice-payment/original-id
@@ -481,8 +470,7 @@
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "The check number that was parsed from the description"} :db/doc "The check number that was parsed from the description"}
;; relations
;; relations
{:db/ident :transaction/vendor {:db/ident :transaction/vendor
:db/valueType :db.type/ref :db/valueType :db.type/ref
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
@@ -498,8 +486,7 @@
{:db/ident :transaction/payment {:db/ident :transaction/payment
:db/valueType :db.type/ref :db/valueType :db.type/ref
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "The payment that this transaction matched to"} :db/doc "The payment that this transaction matched to"}])
])
(def user-schema (def user-schema
[{:db/ident :user/original-id [{:db/ident :user/original-id
@@ -531,12 +518,10 @@
;;enums ;;enums
{:db/ident :user-role/admin} {:db/ident :user-role/admin}
{:db/ident :user-role/user} {:db/ident :user-role/user}
{:db/ident :user-role/none} {:db/ident :user-role/none}])
])
(def base-schema (def base-schema
[ address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema]) [address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema])
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn migrate-vendors [_] (defn migrate-vendors [_]
@@ -657,7 +642,7 @@
(defn apply-pagination [args results] (defn apply-pagination [args results]
{:ids (->> results {:ids (->> results
(drop (or (:start args) 0)) (drop (or (:start args) 0))
(take (or (:count args ) (take (or (:count args)
(:per-page args) (:per-page args)
default-pagination-size)) default-pagination-size))
(map last)) (map last))
@@ -695,10 +680,9 @@
:exception e :exception e
:level :error :level :error
:tx txes) :tx txes)
(throw e) (throw e))))
)))
(defn pull-many [db read ids ] (defn pull-many [db read ids]
(->> (dc/q '[:find (pull ?e r) (->> (dc/q '[:find (pull ?e r)
:in $ [?e ...] r] :in $ [?e ...] r]
db db
@@ -706,7 +690,7 @@
read) read)
(map first))) (map first)))
(defn pull-many-by-id [db read ids ] (defn pull-many-by-id [db read ids]
(into {} (into {}
(map (fn [[e]] (map (fn [[e]]
[(:db/id e) e])) [(:db/id e) e]))
@@ -734,9 +718,6 @@
(defn pull-ref [db k id] (defn pull-ref [db k id]
(:db/id (pull-attr db k id))) (:db/id (pull-attr db k id)))
#_(comment #_(comment
(dc/pull (dc/db conn) '[*] 175921860633685) (dc/pull (dc/db conn) '[*] 175921860633685)
@@ -757,9 +738,7 @@
[{:entity/migration-key 17592234924274, [{:entity/migration-key 17592234924274,
:invoice-expense-account/location nil :invoice-expense-account/location nil
:invoice-expense-account/amount 360.0, :invoice-expense-account/amount 360.0,
:invoice-expense-account/account #:db{:id 92358976759248}}],}) :invoice-expense-account/account #:db{:id 92358976759248}}]})
#_(dc/pull (dc/db conn) auto-ap.datomic.clients 79164837221904) #_(dc/pull (dc/db conn) auto-ap.datomic.clients 79164837221904)
(upsert-entity (dc/db conn) {:client/name "20Twenty - WG Development LLC", (upsert-entity (dc/db conn) {:client/name "20Twenty - WG Development LLC",
@@ -864,9 +843,7 @@
:integration-status/last-updated #inst "2022-08-23T13:09:16.082-00:00", :integration-status/last-updated #inst "2022-08-23T13:09:16.082-00:00",
:integration-status/last-attempt #inst "2022-08-23T13:08:47.018-00:00", :integration-status/last-attempt #inst "2022-08-23T13:08:47.018-00:00",
:integration-status/state :integration-status/state
#:db{:id 101155069755529, :ident :integration-state/success}}}) #:db{:id 101155069755529, :ident :integration-state/success}}}))
)
(defn install-functions [] (defn install-functions []
@(dc/transact conn @(dc/transact conn
@@ -891,7 +868,7 @@
(min (+ backoff-time (rand-int base-timeout)) max-timeout))) (min (+ backoff-time (rand-int base-timeout)) max-timeout)))
(defn transact-with-backoff (defn transact-with-backoff
([tx ] (transact-with-backoff tx 0)) ([tx] (transact-with-backoff tx 0))
([tx attempt] ([tx attempt]
(try (try
@(dc/transact conn tx) @(dc/transact conn tx)
@@ -923,7 +900,6 @@
(into #{} (into #{}
(map :db/id (:user/clients id []))))) (map :db/id (:user/clients id [])))))
(defn query2 [query] (defn query2 [query]
(apply dc/q (:query query) (:args query))) (apply dc/q (:query query) (:args query)))

View File

@@ -66,7 +66,7 @@
(and (not [?v :vendor/account-overrides]) (and (not [?v :vendor/account-overrides])
[?v :vendor/default-account ?e]))] [?v :vendor/default-account ?e]))]
(dc/db conn ) (dc/db conn)
vendor-id vendor-id
client-id client-id
default-read) default-read)
@@ -77,7 +77,7 @@
:in $ ?v r :in $ ?v r
:where [?v :vendor/default-account ?e]] :where [?v :vendor/default-account ?e]]
(dc/db conn ) (dc/db conn)
vendor-id vendor-id
default-read)))) default-read))))
@@ -111,16 +111,14 @@
:args [(re-pattern (str "(?i)" (:name-like args)))]}) :args [(re-pattern (str "(?i)" (:name-like args)))]})
true true
(merge-query {:query {:find ['?sort-default '?e ] (merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :account/name] :where ['[?e :account/name]
'[?e :account/numeric-code ?sort-default]]}}))] '[?e :account/numeric-code ?sort-default]]}}))]
(cond->> (query2 query) (cond->> (query2 query)
true (apply-sort-3 args) true (apply-sort-3 args)
true (apply-pagination args)))) true (apply-pagination args))))
(defn graphql-results [ids db _] (defn graphql-results [ids db _]
(let [results (->> (pull-many db default-read ids) (let [results (->> (pull-many db default-read ids)
(group-by :db/id)) (group-by :db/id))

View File

@@ -14,13 +14,10 @@
(defn <-datomic [x] (defn <-datomic [x]
(->> x (->> x
(map #(update % :bank-account/type :db/ident)) (map #(update % :bank-account/type :db/ident))))
))
(defn get-by-id [id] (defn get-by-id [id]
(->> [(dc/pull (dc/db conn ) default-read id)] (->> [(dc/pull (dc/db conn) default-read id)]
(<-datomic) (<-datomic)
(first))) (first)))

View File

@@ -118,7 +118,6 @@
'[(iol-ion.query/dollars= ?transaction-amount ?amount)]]} '[(iol-ion.query/dollars= ?transaction-amount ?amount)]]}
:args [(:amount args)]}) :args [(:amount args)]})
(:status args) (:status args)
(merge-query {:query {:in ['?status] (merge-query {:query {:in ['?status]
:where ['[?e :payment/status ?status]]} :where ['[?e :payment/status ?status]]}
@@ -137,7 +136,6 @@
true true
(merge-query {:query {:find ['?sort-default '?e]}})))] (merge-query {:query {:find ['?sort-default '?e]}})))]
(cond->> (observable-query query) (cond->> (observable-query query)
true (apply-sort-3 (assoc args :default-asc? false)) true (apply-sort-3 (assoc args :default-asc? false))
true (apply-pagination args))))) true (apply-pagination args)))))

View File

@@ -122,8 +122,6 @@
Long/parseLong Long/parseLong
(#(hash-map :db/id %))))) (#(hash-map :db/id %)))))
(defn exact-match [identifier] (defn exact-match [identifier]
(when (and identifier (not-empty identifier)) (when (and identifier (not-empty identifier))
(some-> (solr/query solr/impl "clients" (some-> (solr/query solr/impl "clients"
@@ -170,7 +168,6 @@
matching-ids) matching-ids)
(set (map :db/id (:clients args)))) (set (map :db/id (:clients args))))
query (cond-> {:query {:find [] query (cond-> {:query {:find []
:in ['$] :in ['$]
:where []} :where []}
@@ -179,7 +176,6 @@
(merge-query {:query {:in ['[?e ...]]} (merge-query {:query {:in ['[?e ...]]}
:args [(set valid-ids)]}) :args [(set valid-ids)]})
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]} (:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
args) args)
@@ -195,7 +191,6 @@
(map cleanse))] (map cleanse))]
results)) results))
(defn get-graphql-page [args] (defn get-graphql-page [args]
(let [db (dc/db conn) (let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]

View File

@@ -76,7 +76,6 @@
'[(iol-ion.query/dollars= ?expected-deposit-total ?total)]]} '[(iol-ion.query/dollars= ?expected-deposit-total ?total)]]}
:args [(:total args)]}) :args [(:total args)]})
(:start (:date-range args)) (:start (:date-range args))
(merge-query {:query {:in '[?start-date] (merge-query {:query {:in '[?start-date]
:where ['[?e :expected-deposit/date ?date] :where ['[?e :expected-deposit/date ?date]

View File

@@ -44,7 +44,6 @@
eas))) eas)))
(rename-keys {:invoice-payment/_invoice :invoice/payments}))) (rename-keys {:invoice-payment/_invoice :invoice/payments})))
(defn raw-graphql-ids (defn raw-graphql-ids
([args] ([args]
(raw-graphql-ids (dc/db conn) args)) (raw-graphql-ids (dc/db conn) args))
@@ -63,37 +62,29 @@
valid-clients]} valid-clients]}
(cond-> {:query {:find [] (cond-> {:query {:find []
:in '[$ [?clients ?start ?end]] :in '[$ [?clients ?start ?end]]
:where '[ :where '[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]]}
[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
]}
:args [db :args [db
[valid-clients [valid-clients
(some-> (:start (:date-range args)) coerce/to-date) (some-> (:start (:date-range args)) coerce/to-date)
(some-> (:end (:date-range args)) coerce/to-date)]]} (some-> (:end (:date-range args)) coerce/to-date)]]}
(:client-id args) (:client-id args)
(merge-query {:query {:in ['?client-id] (merge-query {:query {:in ['?client-id]
:where ['[?e :invoice/client ?client-id]]} :where ['[?e :invoice/client ?client-id]]}
:args [ (:client-id args)]}) :args [(:client-id args)]})
(:client-code args) (:client-code args)
(merge-query {:query {:in ['?client-code] (merge-query {:query {:in ['?client-code]
:where ['[?e :invoice/client ?client-id] :where ['[?e :invoice/client ?client-id]
'[?client-id :client/code ?client-code]]} '[?client-id :client/code ?client-code]]}
:args [ (:client-code args)]}) :args [(:client-code args)]})
(:original-id args) (:original-id args)
(merge-query {:query {:in ['?original-id] (merge-query {:query {:in ['?original-id]
:where [ :where ['[?e :invoice/client ?c]
'[?e :invoice/client ?c]
'[?c :client/original-id ?original-id]]} '[?c :client/original-id ?original-id]]}
:args [ (cond-> (:original-id args) :args [(cond-> (:original-id args)
(string? (:original-id args)) Long/parseLong )]}) (string? (:original-id args)) Long/parseLong)]})
(:start (:due-range args)) (merge-query {:query {:in '[?start-due] (:start (:due-range args)) (merge-query {:query {:in '[?start-due]
:where ['[?e :invoice/due ?due] :where ['[?e :invoice/due ?due]
@@ -105,25 +96,24 @@
'[(<= ?due ?end-due)]]} '[(<= ?due ?end-due)]]}
:args [(coerce/to-date (:end (:due-range args)))]}) :args [(coerce/to-date (:end (:due-range args)))]})
(:import-status args) (:import-status args)
(merge-query {:query {:in ['?import-status] (merge-query {:query {:in ['?import-status]
:where ['[?e :invoice/import-status ?import-status]]} :where ['[?e :invoice/import-status ?import-status]]}
:args [ (keyword "import-status" (:import-status args))]}) :args [(keyword "import-status" (:import-status args))]})
(:status args) (:status args)
(merge-query {:query {:in ['?status] (merge-query {:query {:in ['?status]
:where ['[?e :invoice/status ?status]]} :where ['[?e :invoice/status ?status]]}
:args [ (:status args)]}) :args [(:status args)]})
(:vendor-id args) (:vendor-id args)
(merge-query {:query {:in ['?vendor-id] (merge-query {:query {:in ['?vendor-id]
:where ['[?e :invoice/vendor ?vendor-id]]} :where ['[?e :invoice/vendor ?vendor-id]]}
:args [ (:vendor-id args)]}) :args [(:vendor-id args)]})
(:account-id args) (:account-id args)
(merge-query {:query {:in ['?account-id] (merge-query {:query {:in ['?account-id]
:where ['[?e :invoice/expense-accounts ?iea ?] :where ['[?e :invoice/expense-accounts ?iea ?]
'[?iea :invoice-expense-account/account ?account-id]]} '[?iea :invoice-expense-account/account ?account-id]]}
:args [ (:account-id args)]}) :args [(:account-id args)]})
(:amount-gte args) (:amount-gte args)
(merge-query {:query {:in ['?amount-gte] (merge-query {:query {:in ['?amount-gte]
@@ -151,7 +141,7 @@
(:unresolved args) (:unresolved args)
(merge-query {:query {:in [] (merge-query {:query {:in []
:where ['(or-join [?e] :where ['(or-join [?e]
(not [?e :invoice/expense-accounts ]) (not [?e :invoice/expense-accounts])
(and [?e :invoice/expense-accounts ?ea] (and [?e :invoice/expense-accounts ?ea]
(not [?ea :invoice-expense-account/account])))]} (not [?ea :invoice-expense-account/account])))]}
:args []}) :args []})
@@ -176,12 +166,11 @@
"outstanding-balance" ['[?e :invoice/outstanding-balance ?sort-outstanding-balance]]} "outstanding-balance" ['[?e :invoice/outstanding-balance ?sort-outstanding-balance]]}
args) args)
true true
(merge-query {:query {:find ['?sort-default '?e ]}}) ))] (merge-query {:query {:find ['?sort-default '?e]}})))]
(->> (observable-query query) (->> (observable-query query)
(apply-sort-3 args) (apply-sort-3 args)
(apply-pagination args))))) (apply-pagination args)))))
(defn graphql-results [ids db _] (defn graphql-results [ids db _]
(let [results (->> (pull-many db default-read ids) (let [results (->> (pull-many db default-read ids)
(group-by :db/id)) (group-by :db/id))
@@ -210,8 +199,7 @@
(->> (->>
(dc/q {:find ['?id '?o] (dc/q {:find ['?id '?o]
:in ['$ '[?id ...]] :in ['$ '[?id ...]]
:where ['[?id :invoice/total ?o]] :where ['[?id :invoice/total ?o]]}
}
(dc/db conn) (dc/db conn)
ids) ids)
(map last) (map last)
@@ -226,7 +214,6 @@
outstanding (sum-outstanding ids-to-retrieve) outstanding (sum-outstanding ids-to-retrieve)
total-amount (sum-total-amount ids-to-retrieve)] total-amount (sum-total-amount ids-to-retrieve)]
[(->> (graphql-results ids-to-retrieve db args)) [(->> (graphql-results ids-to-retrieve db args))
matching-count matching-count
outstanding outstanding
@@ -239,9 +226,7 @@
(defn get-multi [ids] (defn get-multi [ids]
(map <-datomic (map <-datomic
(pull-many (dc/db conn) default-read ids ))) (pull-many (dc/db conn) default-read ids)))
(defn find-conflicting [{:keys [:invoice/invoice-number :invoice/vendor :invoice/client :db/id]}] (defn find-conflicting [{:keys [:invoice/invoice-number :invoice/vendor :invoice/client :db/id]}]
@@ -257,26 +242,23 @@
(map first) (map first)
(map <-datomic))) (map <-datomic)))
(defn get-existing-set [] (defn get-existing-set []
(let [vendored-results (set (dc/q {:find ['?vendor '?client '?invoice-number] (let [vendored-results (set (dc/q {:find ['?vendor '?client '?invoice-number]
:in ['$] :in ['$]
:where '[[?e :invoice/invoice-number ?invoice-number] :where '[[?e :invoice/invoice-number ?invoice-number]
[?e :invoice/vendor ?vendor] [?e :invoice/vendor ?vendor]
[?e :invoice/client ?client] [?e :invoice/client ?client]
(not [?e :invoice/status :invoice-status/voided]) (not [?e :invoice/status :invoice-status/voided])]}
]}
(dc/db conn))) (dc/db conn)))
vendorless-results (->> (dc/q {:find ['?client '?invoice-number] vendorless-results (->> (dc/q {:find ['?client '?invoice-number]
:in ['$] :in ['$]
:where '[[?e :invoice/invoice-number ?invoice-number] :where '[[?e :invoice/invoice-number ?invoice-number]
(not [?e :invoice/vendor]) (not [?e :invoice/vendor])
[?e :invoice/client ?client] [?e :invoice/client ?client]
(not [?e :invoice/status :invoice-status/voided]) (not [?e :invoice/status :invoice-status/voided])]}
]}
(dc/db conn)) (dc/db conn))
(mapv (fn [[client invoice-number]] (mapv (fn [[client invoice-number]]
[nil client invoice-number]) ) [nil client invoice-number]))
set)] set)]
(into vendored-results vendorless-results))) (into vendored-results vendorless-results)))
@@ -317,7 +299,7 @@
client-id)))) client-id))))
[schedule-payment-dom] (map first (dc/q '[:find ?dom [schedule-payment-dom] (map first (dc/q '[:find ?dom
:in $ ?v ?c :in $ ?v ?c
:where [?v :vendor/schedule-payment-dom ?sp ] :where [?v :vendor/schedule-payment-dom ?sp]
[?sp :vendor-schedule-payment-dom/client ?c] [?sp :vendor-schedule-payment-dom/client ?c]
[?sp :vendor-schedule-payment-dom/dom ?dom]] [?sp :vendor-schedule-payment-dom/dom ?dom]]
db db

View File

@@ -35,7 +35,7 @@
(some-> (:end (:date-range args)) coerce/to-date)]]} (some-> (:end (:date-range args)) coerce/to-date)]]}
(:only-external args) (:only-external args)
(merge-query {:query {:where ['(not [?e :journal-entry/original-entity ])]}}) (merge-query {:query {:where ['(not [?e :journal-entry/original-entity])]}})
(seq (:external-id-like args)) (seq (:external-id-like args))
(merge-query {:query {:in ['?external-id-like] (merge-query {:query {:in ['?external-id-like]
@@ -53,7 +53,6 @@
:where ['[?e :journal-entry/vendor ?vendor-id]]} :where ['[?e :journal-entry/vendor ?vendor-id]]}
:args [(:vendor-id args)]}) :args [(:vendor-id args)]})
(or (seq (:numeric-code args)) (or (seq (:numeric-code args))
(:bank-account-id args) (:bank-account-id args)
(not-empty (:location args))) (not-empty (:location args)))
@@ -70,7 +69,6 @@
:args [(vec (for [{:keys [from to]} (:numeric-code args)] :args [(vec (for [{:keys [from to]} (:numeric-code args)]
[(or from 0) (or to 99999)]))]}) [(or from 0) (or to 99999)]))]})
(:amount-gte args) (:amount-gte args)
(merge-query {:query {:in ['?amount-gte] (merge-query {:query {:in ['?amount-gte]
:where ['[?e :journal-entry/amount ?a] :where ['[?e :journal-entry/amount ?a]

View File

@@ -43,8 +43,7 @@
:sales-order/source, :sales-order/source,
:sales-order/reference-link, :sales-order/reference-link,
{:sales-order/client [:client/name :db/id :client/code] {:sales-order/client [:client/name :db/id :client/code]
:sales-order/charges [ :sales-order/charges [:charge/type-name,
:charge/type-name,
:charge/total, :charge/total,
:charge/tax, :charge/tax,
:charge/tip, :charge/tip,
@@ -63,7 +62,6 @@
(set/intersection #{(:client-id args)} (set/intersection #{(:client-id args)}
visible-clients) visible-clients)
(:client-code args) (:client-code args)
(set/intersection #{(pull-id db [:client/code (:client-code args)])} (set/intersection #{(pull-id db [:client/code (:client-code args)])}
visible-clients) visible-clients)
@@ -79,7 +77,7 @@
:where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} :where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
:args [db [selected-clients :args [db [selected-clients
(some-> (:start (:date-range args)) c/to-date) (some-> (:start (:date-range args)) c/to-date)
(some-> (:end (:date-range args)) c/to-date )]]} (some-> (:end (:date-range args)) c/to-date)]]}
(:sort args) (add-sorter-fields-2 {"client" ['[?e :sales-order/client ?c] (:sort args) (add-sorter-fields-2 {"client" ['[?e :sales-order/client ?c]
'[?c :client/name ?sort-client]] '[?c :client/name ?sort-client]]

View File

@@ -78,7 +78,6 @@
(merge-query {:query {:find ['?e] (merge-query {:query {:find ['?e]
:where ['[?e :transaction-rule/transaction-approval-status]]}}))] :where ['[?e :transaction-rule/transaction-approval-status]]}}))]
(cond->> (query2 query) (cond->> (query2 query)
true (apply-sort-3 args) true (apply-sort-3 args)
true (apply-pagination args)))) true (apply-pagination args))))
@@ -105,7 +104,7 @@
(defn get-all [] (defn get-all []
(mapv first (mapv first
(dc/q {:find [(list 'pull '?e default-read )] (dc/q {:find [(list 'pull '?e default-read)]
:in ['$] :in ['$]
:where ['[?e :transaction-rule/transaction-approval-status]]} :where ['[?e :transaction-rule/transaction-approval-status]]}
(dc/db conn)))) (dc/db conn))))

View File

@@ -37,7 +37,6 @@
(map first) (map first)
set))) set)))
(defn raw-graphql-ids (defn raw-graphql-ids
([args] (raw-graphql-ids (dc/db conn) args)) ([args] (raw-graphql-ids (dc/db conn) args))
([db args] ([db args]
@@ -87,7 +86,6 @@
:where ['[?e :transaction/vendor ?vendor-id]]} :where ['[?e :transaction/vendor ?vendor-id]]}
:args [(:vendor-id args)]}) :args [(:vendor-id args)]})
(:amount-gte args) (:amount-gte args)
(merge-query {:query {:in ['?amount-gte] (merge-query {:query {:in ['?amount-gte]
:where ['[?e :transaction/amount ?a] :where ['[?e :transaction/amount ?a]

View File

@@ -4,7 +4,7 @@
[datomic.api :as dc] [datomic.api :as dc]
[datomic.api :as d])) [datomic.api :as d]))
(defn find-or-insert! [{:keys [:user/provider :user/provider-id ] :as new-user}] (defn find-or-insert! [{:keys [:user/provider :user/provider-id] :as new-user}]
(let [is-first-user? (not (seq (dc/q [:find '?e (let [is-first-user? (not (seq (dc/q [:find '?e
:in '$ :in '$
:where '[?e :user/provider]] :where '[?e :user/provider]]

View File

@@ -18,7 +18,7 @@
(:vendor/legal-entity-tin-type a) (update :vendor/legal-entity-tin-type :db/ident) (:vendor/legal-entity-tin-type a) (update :vendor/legal-entity-tin-type :db/ident)
(:vendor/legal-entity-1099-type a) (update :vendor/legal-entity-1099-type :db/ident) (:vendor/legal-entity-1099-type a) (update :vendor/legal-entity-1099-type :db/ident)
true (assoc :usage (:vendor-usage/_vendor a)) true (assoc :usage (:vendor-usage/_vendor a))
true (dissoc :vendor-usage/_vendor ))) true (dissoc :vendor-usage/_vendor)))
(defn cleanse [id vendor] (defn cleanse [id vendor]
(let [clients (if-let [clients (limited-clients id)] (let [clients (if-let [clients (limited-clients id)]
@@ -50,7 +50,6 @@
:vendor/plaid-merchant [:db/id :plaid-merchant/name] :vendor/plaid-merchant [:db/id :plaid-merchant/name]
:vendor-usage/_vendor [:vendor-usage/client :vendor-usage/count]}]) :vendor-usage/_vendor [:vendor-usage/client :vendor-usage/count]}])
(defn raw-graphql-ids [db args] (defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find [] (let [query (cond-> {:query {:find []
:in ['$] :in ['$]
@@ -70,7 +69,6 @@
(merge-query {:query {:find ['?e] (merge-query {:query {:find ['?e]
:where ['[?e :vendor/name]]}}))] :where ['[?e :vendor/name]]}}))]
(cond->> (query2 query) (cond->> (query2 query)
true (apply-sort-3 args) true (apply-sort-3 args)
true (apply-pagination args)))) true (apply-pagination args))))
@@ -86,9 +84,7 @@
(update v :usage (fn [usages] (update v :usage (fn [usages]
(->> usages (->> usages
(map (fn [u] {:client-id (:db/id (:vendor-usage/client u)) (map (fn [u] {:client-id (:db/id (:vendor-usage/client u))
:count (:vendor-usage/count u)})))))) :count (:vendor-usage/count u)}))))))))
))
(defn graphql-results [ids db args] (defn graphql-results [ids db args]
(let [results (->> (pull-many db default-read ids) (let [results (->> (pull-many db default-read ids)
@@ -104,9 +100,7 @@
(let [db (dc/db conn) (let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
[(->> (graphql-results ids-to-retrieve db args)) [(->> (graphql-results ids-to-retrieve db args))
matching-count]) matching-count]))
)
(defn get-graphql-by-id [args id] (defn get-graphql-by-id [args id]
(->> (dc/q {:find [(list 'pull '?e default-read)] (->> (dc/q {:find [(list 'pull '?e default-read)]
@@ -139,7 +133,6 @@
(map <-datomic) (map <-datomic)
(first))) (first)))
(defn terms-for-client-id [vendor client-id] (defn terms-for-client-id [vendor client-id]
(or (or
(->> (->>

View File

@@ -20,16 +20,15 @@
:body (json/write-str {"query" (v/graphql-query q)}) :body (json/write-str {"query" (v/graphql-query q)})
:as :json}) :as :json})
:body :body
:data :data))
))
(defn get-caterers [integration] (defn get-caterers [integration]
(:caterers (query integration {:venia/queries [{:query/data (:caterers (query integration {:venia/queries [{:query/data
[:caterers [:name :uuid [:address [:name :street]]]]}]} ))) [:caterers [:name :uuid [:address [:name :street]]]]}]})))
(defn get-subscriptions [integration] (defn get-subscriptions [integration]
(->> (query integration {:venia/queries [{:query/data (->> (query integration {:venia/queries [{:query/data
[:subscribers [:id [:subscriptions [:parentId :parentEntity :eventEntity :eventKey]] ]]}]} ) [:subscribers [:id [:subscriptions [:parentId :parentEntity :eventEntity :eventKey]]]]}]})
:subscribers :subscribers
first first
:subscriptions)) :subscriptions))
@@ -94,7 +93,6 @@
:eventKey 'cancelled}} :eventKey 'cancelled}}
[[:subscription [:parentId :parentEntity :eventEntity :eventKey]]]]]}))))) [[:subscription [:parentId :parentEntity :eventEntity :eventKey]]]]]})))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn upsert-ezcater (defn upsert-ezcater
([] (upsert-ezcater (get-integrations))) ([] (upsert-ezcater (get-integrations)))
@@ -121,7 +119,6 @@
{:client/_ezcater-locations [:client/code]}]}] {:client/_ezcater-locations [:client/code]}]}]
[:ezcater-caterer/uuid caterer-uuid])) [:ezcater-caterer/uuid caterer-uuid]))
(defn round-carry-cents [f] (defn round-carry-cents [f]
(with-precision 2 (double (.setScale (bigdec f) 2 java.math.RoundingMode/HALF_UP)))) (with-precision 2 (double (.setScale (bigdec f) 2 java.math.RoundingMode/HALF_UP))))
@@ -139,7 +136,7 @@
(* commision% (* commision%
0.01M 0.01M
(+ (+
(-> order :totals :subTotal :subunits ) (-> order :totals :subTotal :subunits)
(reduce + (reduce +
0 0
(map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))) (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))))
@@ -148,8 +145,8 @@
(round-carry-cents (round-carry-cents
(* 0.000299M (* 0.000299M
(+ (+
(-> order :totals :subTotal :subunits ) (-> order :totals :subTotal :subunits)
(-> order :totals :salesTax :subunits ) (-> order :totals :salesTax :subunits)
(reduce + (reduce +
0 0
(map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))) (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))
@@ -157,7 +154,7 @@
(defn order->sales-order [{{:keys [timestamp]} :event {:keys [orderItems]} :catererCart :keys [client-code client-location uuid] :as order}] (defn order->sales-order [{{:keys [timestamp]} :event {:keys [orderItems]} :catererCart :keys [client-code client-location uuid] :as order}]
(let [adjustment (round-carry-cents (- (+ (-> order :totals :subTotal :subunits (* 0.01)) (let [adjustment (round-carry-cents (- (+ (-> order :totals :subTotal :subunits (* 0.01))
(-> order :totals :salesTax :subunits (* 0.01))) (-> order :totals :salesTax :subunits (* 0.01)))
(-> order :catererCart :totals :catererTotalDue ) (-> order :catererCart :totals :catererTotalDue)
(commision order) (commision order)
(ccp-fee order))) (ccp-fee order)))
service-charge (+ (commision order) (ccp-fee order)) service-charge (+ (commision order) (ccp-fee order))
@@ -168,7 +165,7 @@
:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid) :external-id (str "ezcater/order/" client-code "-" client-location "-" uuid)
:client [:client/code client-code] :client [:client/code client-code]
:location client-location :location client-location
:reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid )) :reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid))
:line-items [#:order-line-item :line-items [#:order-line-item
{:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0) {:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0)
:item-name "EZCater Catering" :item-name "EZCater Catering"
@@ -200,7 +197,6 @@
:returns 0.0 :returns 0.0
:vendor :vendor/ccp-ezcater})) :vendor :vendor/ccp-ezcater}))
(defn get-by-id [integration id] (defn get-by-id [integration id]
(query (query
integration integration
@@ -231,27 +227,20 @@
[:currency [:currency
:subunits]]]]]] :subunits]]]]]]
[:totals [[:customerTotalDue [:totals [[:customerTotalDue
[ [:currency
:currency :subunits]]
:subunits
]]
[:pointOfSaleIntegrationFee [:pointOfSaleIntegrationFee
[ [:currency
:currency :subunits]]
:subunits
]]
[:tip [:tip
[:currency [:currency
:subunits]] :subunits]]
[:salesTax [:salesTax
[ [:currency
:currency :subunits]]
:subunits
]]
[:salesTaxRemittance [:salesTaxRemittance
[:currency [:currency
:subunits :subunits]]
]]
[:subTotal [:subTotal
[:currency [:currency
:subunits]]]]]]]})) :subunits]]]]]]]}))

View File

@@ -34,8 +34,7 @@
(clojure.lang IPersistentMap))) (clojure.lang IPersistentMap)))
(def integreat-schema (def integreat-schema
{ {:scalars {:id {:parse #(cond (number? %)
:scalars {:id {:parse #(cond (number? %)
% %
% %
@@ -86,8 +85,7 @@
(str %) (str %)
%)}} %)}}
:objects :objects
{ {:message
:message
{:fields {:message {:type 'String}}} {:fields {:message {:type 'String}}}
:search_result :search_result
@@ -128,7 +126,6 @@
:email {:type 'String} :email {:type 'String}
:phone {:type 'String}}} :phone {:type 'String}}}
:address :address
{:fields {:id {:type :id} {:fields {:id {:type :id}
:street1 {:type 'String} :street1 {:type 'String}
@@ -184,7 +181,6 @@
:legal_entity_tin_type {:type :tin_type} :legal_entity_tin_type {:type :tin_type}
:legal_entity_1099_type {:type :type_1099}}} :legal_entity_1099_type {:type :type_1099}}}
:reminder :reminder
{:fields {:id {:type 'Int} {:fields {:id {:type 'Int}
:email {:type 'String} :email {:type 'String}
@@ -222,8 +218,6 @@
:accounts {:type '(list :percentage_account)} :accounts {:type '(list :percentage_account)}
:transaction_approval_status {:type :transaction_approval_status}}} :transaction_approval_status {:type :transaction_approval_status}}}
:user :user
{:fields {:id {:type :id} {:fields {:id {:type :id}
:name {:type 'String} :name {:type 'String}
@@ -264,18 +258,12 @@
:start {:type 'Int} :start {:type 'Int}
:end {:type 'Int}}} :end {:type 'Int}}}
:transaction_rule_page {:fields {:transaction_rules {:type '(list :transaction_rule)} :transaction_rule_page {:fields {:transaction_rules {:type '(list :transaction_rule)}
:count {:type 'Int} :count {:type 'Int}
:total {:type 'Int} :total {:type 'Int}
:start {:type 'Int} :start {:type 'Int}
:end {:type 'Int}}} :end {:type 'Int}}}
:vendor_page {:fields {:vendors {:type '(list :vendor)} :vendor_page {:fields {:vendors {:type '(list :vendor)}
:count {:type 'Int} :count {:type 'Int}
:total {:type 'Int} :total {:type 'Int}
@@ -321,7 +309,6 @@
:args {:transaction_id {:type :id}} :args {:transaction_id {:type :id}}
:resolve :get-transaction-rule-matches} :resolve :get-transaction-rule-matches}
:test_transaction_rule {:type '(list :transaction) :test_transaction_rule {:type '(list :transaction)
:args {:transaction_rule {:type :edit_transaction_rule}} :args {:transaction_rule {:type :edit_transaction_rule}}
:resolve :test-transaction-rule} :resolve :test-transaction-rule}
@@ -338,7 +325,6 @@
:args {:client_id {:type :id}} :args {:client_id {:type :id}}
:resolve :get-cash-flow} :resolve :get-cash-flow}
:all_accounts {:type '(list :account) :all_accounts {:type '(list :account)
:args {} :args {}
:resolve :get-all-accounts} :resolve :get-all-accounts}
@@ -353,10 +339,6 @@
:vendor_id {:type :id}} :vendor_id {:type :id}}
:resolve :search-account} :resolve :search-account}
:yodlee_merchants {:type '(list :yodlee_merchant) :yodlee_merchants {:type '(list :yodlee_merchant)
:args {} :args {}
:resolve :get-yodlee-merchants} :resolve :get-yodlee-merchants}
@@ -376,9 +358,6 @@
:description {:type 'String}} :description {:type 'String}}
:resolve :get-transaction-rule-page} :resolve :get-transaction-rule-page}
:vendor {:type :vendor_page :vendor {:type :vendor_page
:args {:name_like {:type 'String} :args {:name_like {:type 'String}
:start {:type 'Int} :start {:type 'Int}
@@ -395,8 +374,7 @@
:resolve :account-for-vendor}} :resolve :account-for-vendor}}
:input-objects :input-objects
{ {:sort_item
:sort_item
{:fields {:sort_key {:type 'String} {:fields {:sort_key {:type 'String}
:sort_name {:type 'String} :sort_name {:type 'String}
:asc {:type 'Boolean}}} :asc {:type 'Boolean}}}
@@ -495,8 +473,7 @@
:name {:type 'String} :name {:type 'String}
:client_overrides {:type '(list :edit_account_client_override)}}}} :client_overrides {:type '(list :edit_account_client_override)}}}}
:enums { :enums {:processor {:values [{:enum-value :na}
:processor {:values [{:enum-value :na}
{:enum-value :doordash} {:enum-value :doordash}
{:enum-value :koala} {:enum-value :koala}
{:enum-value :ezcater} {:enum-value :ezcater}
@@ -533,9 +510,7 @@
{:enum-value :equity} {:enum-value :equity}
{:enum-value :revenue}]}} {:enum-value :revenue}]}}
:mutations :mutations
{ {:delete_transaction_rule
:delete_transaction_rule
{:type :id {:type :id
:args {:transaction_rule_id {:type :id}} :args {:transaction_rule_id {:type :id}}
:resolve :mutation/delete-transaction-rule} :resolve :mutation/delete-transaction-rule}
@@ -553,9 +528,7 @@
:upsert_transaction_rule :upsert_transaction_rule
{:type :transaction_rule {:type :transaction_rule
:args {:transaction_rule {:type :edit_transaction_rule}} :args {:transaction_rule {:type :edit_transaction_rule}}
:resolve :mutation/upsert-transaction-rule} :resolve :mutation/upsert-transaction-rule}}})
}})
(defn snake->kebab [s] (defn snake->kebab [s]
(str/replace s #"_" "-")) (str/replace s #"_" "-"))
@@ -581,8 +554,7 @@
node)) node))
m)) m))
(defn get-expense-account-stats [_ {:keys [client_id]} _]
(defn get-expense-account-stats [_ {:keys [client_id] } _]
(let [query (cond-> {:query {:find ['?account '?account-name '(sum ?amount)] (let [query (cond-> {:query {:find ['?account '?account-name '(sum ?amount)]
:in ['$] :in ['$]
:where []} :where []}
@@ -601,7 +573,7 @@
(for [[account-id account-name total] result] (for [[account-id account-name total] result]
{:account {:id account-id :name account-name} :total total}))) {:account {:id account-id :name account-name} :total total})))
(defn get-invoice-stats [_ {:keys [client_id] } _] (defn get-invoice-stats [_ {:keys [client_id]} _]
(let [query (cond-> {:query {:find ['?name '(sum ?outstanding-balance) '(sum ?total)] (let [query (cond-> {:query {:find ['?name '(sum ?outstanding-balance) '(sum ?total)]
:in ['$] :in ['$]
:where []} :where []}
@@ -615,7 +587,7 @@
'[?i :invoice/total ?total] '[?i :invoice/total ?total]
'[?i :invoice/due ?date] '[?i :invoice/due ?date]
'[(.toInstant ^java.util.Date ?date) ?d2] '[(.toInstant ^java.util.Date ?date) ?d2]
'[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3] '[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2) ?d3]
'(or-join [?d3 ?name] '(or-join [?d3 ?name]
(and [(<= ?d3 0)] (and [(<= ?d3 0)]
[(ground :due) ?name]) [(ground :due) ?name])
@@ -629,7 +601,7 @@
(group-by first))] (group-by first))]
(for [[id name] [[:due "Due"] [:due-30 "0-30 days"] [:due-60 "31-60 days"] [:due-later ">60 days"]] (for [[id name] [[:due "Due"] [:due-30 "0-30 days"] [:due-60 "31-60 days"] [:due-later ">60 days"]]
:let [[[_ outstanding-balance total] ] (id result nil) :let [[[_ outstanding-balance total]] (id result nil)
outstanding-balance (or outstanding-balance 0) outstanding-balance (or outstanding-balance 0)
total (or total 0)]] total (or total 0)]]
{:name name :unpaid outstanding-balance :paid (if (= :due id) {:name name :unpaid outstanding-balance :paid (if (= :due id)
@@ -652,7 +624,7 @@
(defn get-cash-flow [_ {:keys [client_id]} _] (defn get-cash-flow [_ {:keys [client_id]} _]
(when client_id (when client_id
(let [{:client/keys [week-a-credits week-a-debits week-b-credits week-b-debits forecasted-transactions ]} (dc/pull (dc/db conn) '[*] client_id) (let [{:client/keys [week-a-credits week-a-debits week-b-credits week-b-debits forecasted-transactions]} (dc/pull (dc/db conn) '[*] client_id)
total-cash (reduce total-cash (reduce
(fn [total [credit debit]] (fn [total [credit debit]]
(- (+ total credit) (- (+ total credit)
@@ -736,12 +708,10 @@
(take (* 7 4) (time/day-of-week-seq 1))) (take (* 7 4) (time/day-of-week-seq 1)))
(filter #(< (:amount %) 0) forecasted-transactions))}))) (filter #(< (:amount %) 0) forecasted-transactions))})))
(def schema (def schema
(-> integreat-schema (-> integreat-schema
(attach-tracing-resolvers (attach-tracing-resolvers
{ {:get-all-accounts gq-accounts/get-all-graphql
:get-all-accounts gq-accounts/get-all-graphql
:get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page :get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page
:get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches :get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches
:get-expense-account-stats get-expense-account-stats :get-expense-account-stats get-expense-account-stats
@@ -772,8 +742,6 @@
gq-sales-orders/attach gq-sales-orders/attach
schema/compile)) schema/compile))
(defn simplify (defn simplify
"Converts all ordered maps nested within the map into standard hash maps, and "Converts all ordered maps nested within the map into standard hash maps, and
sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems." sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems."
@@ -805,7 +773,7 @@
(query id q nil)) (query id q nil))
([id q v] ([id q v]
(statsd/increment "query.graphql.count" {:tags #{(str "query:" (query-name q))}}) (statsd/increment "query.graphql.count" {:tags #{(str "query:" (query-name q))}})
(statsd/time! [(str "query.graphql.time" ) {:tags #{(str "query:" (query-name q))}}] (statsd/time! [(str "query.graphql.time") {:tags #{(str "query:" (query-name q))}}]
(mu/with-context {:query-name (query-name q) :user id :query q} (mu/with-context {:query-name (query-name q) :user id :query q}
(mu/trace ::executing-query (mu/trace ::executing-query
[] []

View File

@@ -18,7 +18,6 @@
[iol-ion.tx :refer [random-tempid]] [iol-ion.tx :refer [random-tempid]]
[com.brunobonacci.mulog :as mu])) [com.brunobonacci.mulog :as mu]))
(defn get-all-graphql [context args _] (defn get-all-graphql [context args _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))

View File

@@ -97,7 +97,6 @@
[:line {:line-width 0.15 :color [50 50 50]}]] [:line {:line-width 0.15 :color [50 50 50]}]]
[:cell {:colspan 3}]] [:cell {:colspan 3}]]
[[:cell {:size 9 :leading 11.5} "\n\n\n\n\nMEMO"] [[:cell {:size 9 :leading 11.5} "\n\n\n\n\nMEMO"]
[:cell {:colspan 5 :leading 11.5} (split-memo memo) [:cell {:colspan 5 :leading 11.5} (split-memo memo)
[:line {:line-width 0.15 :color [50 50 50]}]] [:line {:line-width 0.15 :color [50 50 50]}]]
@@ -186,8 +185,6 @@
:payment/pdf-data :payment/pdf-data
(edn/read-string) (edn/read-string)
make-check-pdf)] make-check-pdf)]
(s3/put-object :bucket-name (:data-bucket env) (s3/put-object :bucket-name (:data-bucket env)
:key (:payment/s3-key check) :key (:payment/s3-key check)
@@ -277,7 +274,6 @@
(conj payment) (conj payment)
(into (invoice-payments invoices invoice-amounts))))) (into (invoice-payments invoices invoice-amounts)))))
(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts date] (defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts date]
(when (<= (->> invoices (when (<= (->> invoices
(map (comp invoice-amounts :db/id)) (map (comp invoice-amounts :db/id))
@@ -297,7 +293,6 @@
(conj payment) (conj payment)
(into (invoice-payments invoices invoice-amounts))))) (into (invoice-payments invoices invoice-amounts)))))
(defmethod invoices->entities :payment-type/balance-credit [invoices invoice-amounts] (defmethod invoices->entities :payment-type/balance-credit [invoices invoice-amounts]
(when (<= (->> invoices (when (<= (->> invoices
(map (comp invoice-amounts :db/id)) (map (comp invoice-amounts :db/id))
@@ -488,7 +483,6 @@
{:s3-url nil {:s3-url nil
:invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args)))}))) :invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args)))})))
(defn void-payment [context {id :payment_id} _] (defn void-payment [context {id :payment_id} _]
(let [check (d-checks/get-by-id id)] (let [check (d-checks/get-by-id id)]
(assert (or (= :payment-status/pending (:payment/status check)) (assert (or (= :payment-status/pending (:payment/status check))
@@ -549,7 +543,6 @@
:invoice-status/unpaid)}]])))))))) :invoice-status/unpaid)}]]))))))))
id)) id))
(defn void-payments [context args _] (defn void-payments [context args _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [args (assoc args :clients (:clients context)) (let [args (assoc args :clients (:clients context))
@@ -607,7 +600,6 @@
0.001)) 0.001))
invoices) invoices)
total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid)) total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid))
_ (when (<= total-to-pay 0.001) _ (when (<= total-to-pay 0.001)
(assert-failure "You must select invoices that need to be paid.")) (assert-failure "You must select invoices that need to be paid."))
@@ -637,8 +629,6 @@
[total-to-pay []]))) [total-to-pay []])))
(into {})) (into {}))
vendor-id (:db/id (:invoice/vendor (first invoices))) vendor-id (:db/id (:invoice/vendor (first invoices)))
payment {:db/id (str vendor-id) payment {:db/id (str vendor-id)
:payment/amount total-to-pay :payment/amount total-to-pay
@@ -751,7 +741,6 @@
{:enum-value :pending} {:enum-value :pending}
{:enum-value :cleared}]}}) {:enum-value :cleared}]}})
(def resolvers (def resolvers
{:get-potential-payments get-potential-payments {:get-potential-payments get-potential-payments
:get-payment-page get-payment-page :get-payment-page get-payment-page

View File

@@ -47,13 +47,6 @@
bank-accounts))))))] bank-accounts))))))]
(result->page clients clients-count :clients (:filters args)))) (result->page clients clients-count :clients (:filters args))))
(def objects (def objects
{:location_match {:location_match
{:fields {:location {:type 'String} {:fields {:location {:type 'String}
@@ -109,8 +102,6 @@
:start {:type 'Int} :start {:type 'Int}
:end {:type 'Int}}} :end {:type 'Int}}}
:bank_account :bank_account
{:fields {:id {:type :id} {:fields {:id {:type :id}
:integration_status {:type :integration_status} :integration_status {:type :integration_status}
@@ -139,9 +130,7 @@
:forecasted_transaction {:fields {:identifier {:type 'String} :forecasted_transaction {:fields {:identifier {:type 'String}
:id {:type :id} :id {:type :id}
:day_of_month {:type 'Int} :day_of_month {:type 'Int}
:amount {:type :money}}} :amount {:type :money}}}})
})
(def queries (def queries
{:client {:type '(list :client) {:client {:type '(list :client)
@@ -158,12 +147,12 @@
{}) {})
(def input-objects (def input-objects
{ :client_filters {:client_filters
{:fields {:code {:type 'String} {:fields {:code {:type 'String}
:name_like {:type 'String} :name_like {:type 'String}
:start {:type 'Int} :start {:type 'Int}
:per_page {:type 'Int} :per_page {:type 'Int}
:sort {:type '(list :sort_item)}}} }) :sort {:type '(list :sort_item)}}}})
(def enums (def enums
{:bank_account_type {:values [{:enum-value :check} {:bank_account_type {:values [{:enum-value :check}
@@ -173,8 +162,7 @@
(def resolvers (def resolvers
{:get-client get-client {:get-client get-client
:get-admin-client get-admin-client :get-admin-client get-admin-client
:get-client-page get-client-page }) :get-client-page get-client-page})
(defn attach [schema] (defn attach [schema]
(-> (->

View File

@@ -17,7 +17,6 @@
{:name name {:name name
:id id})) :id id}))
(def objects (def objects
{:ezcater_caterer {:fields {:name {:type 'String} {:ezcater_caterer {:fields {:name {:type 'String}
:id {:type :id}}}}) :id {:type :id}}}})

View File

@@ -40,7 +40,6 @@
(merge-query {:query {:find ['?e] (merge-query {:query {:find ['?e]
:where ['[?e :import-batch/date]]}}))] :where ['[?e :import-batch/date]]}}))]
(cond->> (query2 query) (cond->> (query2 query)
true (apply-sort-3 args) true (apply-sort-3 args)
true (apply-pagination args)))) true (apply-pagination args))))
@@ -66,7 +65,6 @@
(map #(update % :import-batch/date coerce/to-date-time))) (map #(update % :import-batch/date coerce/to-date-time)))
matching-count :data args))) matching-count :data args)))
(defn attach [schema] (defn attach [schema]
(-> (->
(merge-with merge schema (merge-with merge schema
@@ -83,9 +81,7 @@
:count {:type 'Int} :count {:type 'Int}
:total {:type 'Int} :total {:type 'Int}
:start {:type 'Int} :start {:type 'Int}
:end {:type 'Int}}} :end {:type 'Int}}}}
}
:queries {:import_batch_page {:type :import_batch_page :queries {:import_batch_page {:type :import_batch_page
:args {:filters {:type :import_batch_filters}} :args {:filters {:type :import_batch_filters}}

View File

@@ -174,8 +174,6 @@
(let [error (str "Expense account total (" expense-account-total ") does not equal invoice total (" total ")")] (let [error (str "Expense account total (" expense-account-total ") does not equal invoice total (" total ")")]
(throw (ex-info error {:validation-error error})))))) (throw (ex-info error {:validation-error error}))))))
(defn add-invoice [context {{:keys [expense_accounts client_id vendor_id] :as in} :invoice} _] (defn add-invoice [context {{:keys [expense_accounts client_id vendor_id] :as in} :invoice} _]
(assert-no-conflicting in) (assert-no-conflicting in)
(assert-can-see-client (:id context) client_id) (assert-can-see-client (:id context) client_id)
@@ -193,8 +191,6 @@
(when-not ((set (map :db/id (:client/bank-accounts (d-clients/get-by-id client-id)))) bank-account-id) (when-not ((set (map :db/id (:client/bank-accounts (d-clients/get-by-id client-id)))) bank-account-id)
(throw (ex-info (str "Bank account does not belong to client") {:validation-error "Bank account does not belong to client."})))) (throw (ex-info (str "Bank account does not belong to client") {:validation-error "Bank account does not belong to client."}))))
(defn add-and-print-invoice [context {{:keys [total client_id vendor_id] :as in} :invoice bank-account-id :bank_account_id type :type} _] (defn add-and-print-invoice [context {{:keys [total client_id vendor_id] :as in} :invoice bank-account-id :bank_account_id type :type} _]
(mu/trace ::validating-invoice [:invoice in] (mu/trace ::validating-invoice [:invoice in]
(do (do
@@ -261,7 +257,6 @@
(-> (d-invoices/get-by-id id) (->graphql (:id context))))) (-> (d-invoices/get-by-id id) (->graphql (:id context)))))
(defn get-ids-matching-filters [args] (defn get-ids-matching-filters [args]
(let [ids (some-> args (let [ids (some-> args
:filters :filters
@@ -448,8 +443,6 @@
[])] [])]
accounts))) accounts)))
(defn bulk-change-invoices [context args _] (defn bulk-change-invoices [context args _]
(assert-admin (:id context)) (assert-admin (:id context))
(when-not (:client_id args) (when-not (:client_id args)

View File

@@ -55,10 +55,8 @@
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))
[journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args)) [journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args))
:per-page Integer/MAX_VALUE :per-page Integer/MAX_VALUE
:clients (:clients context))) :clients (:clients context)))]
]
{:csv_content_b64 (Base64/encodeBase64String {:csv_content_b64 (Base64/encodeBase64String
(.getBytes (.getBytes
(with-open [w (java.io.StringWriter.)] (with-open [w (java.io.StringWriter.)]
@@ -83,22 +81,19 @@
(-> li :journal-entry-line/account :bank-account/numeric-code)) (-> li :journal-entry-line/account :bank-account/numeric-code))
(or (-> li :journal-entry-line/account :account/name) (or (-> li :journal-entry-line/account :account/name)
(-> li :journal-entry-line/account :bank-account/name)) (-> li :journal-entry-line/account :bank-account/name))
(some-> account-type name ) (some-> account-type name)
(-> li :journal-entry-line/debit) (-> li :journal-entry-line/debit)
(-> li :journal-entry-line/credit) (-> li :journal-entry-line/credit)
(if (#{:account-type/asset (if (#{:account-type/asset
:account-type/dividend :account-type/dividend
:account-type/expense} account-type) :account-type/expense} account-type)
(- (or (-> li :journal-entry-line/debit) 0.0) (or (-> li :journal-entry-line/credit) 0.0)) (- (or (-> li :journal-entry-line/debit) 0.0) (or (-> li :journal-entry-line/credit) 0.0))
(- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0))) (- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0)))]))
])) (:journal-entry/line-items j))))))
(:journal-entry/line-items j))
))))
:quote? (constantly true)) :quote? (constantly true))
(.toString w))))})) (.toString w))))}))
(defn roll-up-until (defn roll-up-until
([lookup-account all-ledger-entries end-date] ([lookup-account all-ledger-entries end-date]
(roll-up-until lookup-account all-ledger-entries end-date nil)) (roll-up-until lookup-account all-ledger-entries end-date nil))
@@ -115,8 +110,7 @@
(-> acc (-> acc
(update-in [[location account] :debit] (fnil + 0.0) debit) (update-in [[location account] :debit] (fnil + 0.0) debit)
(update-in [[location account] :credit] (fnil + 0.0) credit) (update-in [[location account] :credit] (fnil + 0.0) credit)
(update-in [[location account] :count] (fnil + 0) 1)) (update-in [[location account] :count] (fnil + 0) 1)))
)
{}) {})
(reduce-kv (reduce-kv
(fn [acc [location account-id] {:keys [debit credit count]}] (fn [acc [location account-id] {:keys [debit credit count]}]
@@ -152,9 +146,9 @@
(or [?b :bank-account/include-in-reports true] (or [?b :bank-account/include-in-reports true]
(not [?b :bank-account/include-in-reports]))) (not [?b :bank-account/include-in-reports])))
(not [?i :transaction/bank-account]))) (not [?i :transaction/bank-account])))
(not [?e :journal-entry/original-entity ])) (not [?e :journal-entry/original-entity]))
[(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account] [(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account]
[(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit ] [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
[(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit] [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
[(get-else $ ?jel :journal-entry-line/location "") ?location]]} [(get-else $ ?jel :journal-entry-line/location "") ?location]]}
(dc/db conn) client-id) (dc/db conn) client-id)
@@ -182,16 +176,15 @@
(alog/info ::balance-sheet :params args) (alog/info ::balance-sheet :params args)
(cond-> {:balance-sheet-accounts (mapcat (cond-> {:balance-sheet-accounts (mapcat
#(roll-up-until (lookup-account %) (all-ledger-entries %) end-date ) #(roll-up-until (lookup-account %) (all-ledger-entries %) end-date)
client-ids) client-ids)}
}
(:include_comparison args) (assoc :comparable-balance-sheet-accounts (mapcat (:include_comparison args) (assoc :comparable-balance-sheet-accounts (mapcat
#(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date ) #(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date)
client-ids)) client-ids))
true ->graphql))) true ->graphql)))
(defn get-profit-and-loss-raw [client-ids periods] (defn get-profit-and-loss-raw [client-ids periods]
(let [ all-ledger-entries (->> client-ids (let [all-ledger-entries (->> client-ids
(map (fn [client-id] (map (fn [client-id]
[client-id (full-ledger-for-client client-id)])) [client-id (full-ledger-for-client client-id)]))
(into {})) (into {}))
@@ -203,7 +196,7 @@
(->> periods (->> periods
(mapv (fn [{:keys [start end]}] (mapv (fn [{:keys [start end]}]
{:accounts (mapcat {:accounts (mapcat
#(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start) ) #(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start))
client-ids)})))}))) client-ids)})))})))
(defn get-profit-and-loss [context args _] (defn get-profit-and-loss [context args _]
@@ -216,12 +209,9 @@
(assert-can-see-client (:id context) client-id)) (assert-can-see-client (:id context) client-id))
_ (when (and (:include_deltas args) _ (when (and (:include_deltas args)
(:column_per_location args)) (:column_per_location args))
(throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"}))) ] (throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"})))]
(get-profit-and-loss-raw client-ids (:periods args)))) (get-profit-and-loss-raw client-ids (:periods args))))
;; profit and loss based off of index ;; profit and loss based off of index
#_(defn get-profit-and-loss [context args _] #_(defn get-profit-and-loss [context args _]
(let [client-id (:client_id args) (let [client-id (:client_id args)
@@ -296,8 +286,7 @@
:debits 0 :debits 0
:credits 0 :credits 0
:amount (- (or (:journal-entry-line/running-balance end-point) 0.0) :amount (- (or (:journal-entry-line/running-balance end-point) 0.0)
(or (:journal-entry-line/running-balance start-point) 0.0)) (or (:journal-entry-line/running-balance start-point) 0.0))}
}
((lookup-account c) a))]))) ((lookup-account c) a))])))
all-used-account-locations)}))))}))) all-used-account-locations)}))))})))
@@ -320,7 +309,6 @@
(->graphql result))) (->graphql result)))
(defn assoc-error [f] (defn assoc-error [f]
(fn [entry] (fn [entry]
(try (try
@@ -443,7 +431,7 @@
(assoc-error (fn [entry] (assoc-error (fn [entry]
(let [vendor (all-vendors (:vendor_name entry))] (let [vendor (all-vendors (:vendor_name entry))]
(when-not (client-locked-lookup (:client_code entry)) (when-not (client-locked-lookup (:client_code entry))
(throw (ex-info (str "Client '" (:client_code entry )"' not found.") {:status :error}) )) (throw (ex-info (str "Client '" (:client_code entry) "' not found.") {:status :error})))
(when-not vendor (when-not vendor
(throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error}))) (throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry)) (when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
@@ -542,19 +530,17 @@
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external_id x)]]) retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external_id x)]])
success) success)
ignore-retraction (->> ignored ignore-retraction (->> ignored
(map :external_id ) (map :external_id)
(dc/q '[:find ?je (dc/q '[:find ?je
:in $ [?ei ...] :in $ [?ei ...]
:where [?je :journal-entry/external-id ?ei]] :where [?je :journal-entry/external-id ?ei]]
(dc/db conn) (dc/db conn))
)
(map first) (map first)
(map (fn [je] [:db/retractEntity je])))] (map (fn [je] [:db/retractEntity je])))]
(alog/info ::manual-import (alog/info ::manual-import
:errors (count errors) :errors (count errors)
:sample (take 3 errors)) :sample (take 3 errors))
(mu/trace ::retraction-tx (mu/trace ::retraction-tx
[:count (count retraction)] [:count (count retraction)]
(audit-transact-batch retraction (:id context))) (audit-transact-batch retraction (:id context)))
@@ -582,7 +568,6 @@
:errors (map (fn [x] {:external_id (:external_id x) :errors (map (fn [x] {:external_id (:external_id x)
:error (:error x)}) errors)})) :error (:error x)}) errors)}))
(defn get-journal-detail-report [context input _] (defn get-journal-detail-report [context input _]
(let [category-totals (atom {}) (let [category-totals (atom {})
base-categories (into [] base-categories (into []
@@ -597,8 +582,8 @@
:clients [{:db/id client-id}]) :clients [{:db/id client-id}])
{:filters {:location location {:filters {:location location
:date_range (:date_range input) :date_range (:date_range input)
:from_numeric_code (l-reports/min-numeric-code category ) :from_numeric_code (l-reports/min-numeric-code category)
:to_numeric_code (l-reports/max-numeric-code category ) :to_numeric_code (l-reports/max-numeric-code category)
:per_page Integer/MAX_VALUE}} :per_page Integer/MAX_VALUE}}
nil) nil)
:journal_entries :journal_entries
@@ -608,9 +593,8 @@
(when-let [account (account-lookup (:id (:account jel)))] (when-let [account (account-lookup (:id (:account jel)))]
(and (and
(l-reports/account-belongs-in-category? (:numeric_code account) category) (l-reports/account-belongs-in-category? (:numeric_code account) category)
(= location (:location jel))))) (= location (:location jel))))))
) (map (fn [jel]
(map (fn [jel ]
{:date (:date je) {:date (:date je)
:debit (:debit jel) :debit (:debit jel)
:credit (:credit jel) :credit (:credit jel)
@@ -621,7 +605,7 @@
(into [])) (into []))
_ (swap! category-totals assoc-in [client-id location category] _ (swap! category-totals assoc-in [client-id location category]
(- (or (reduce + 0.0 (map #(or (:credit %) 0.0) all-journal-entries)) 0.0) (- (or (reduce + 0.0 (map #(or (:credit %) 0.0) all-journal-entries)) 0.0)
(or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)) ) (or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)))
journal-entries-by-account (group-by #(account-lookup (get-in % [:account :id])) all-journal-entries)] journal-entries-by-account (group-by #(account-lookup (get-in % [:account :id])) all-journal-entries)]
[account journal-entries] (conj (vec journal-entries-by-account) [nil all-journal-entries]) [account journal-entries] (conj (vec journal-entries-by-account) [nil all-journal-entries])
:let [journal-entries (first (reduce :let [journal-entries (first (reduce
@@ -675,15 +659,12 @@
line))}] line))}]
result)) result))
(defn journal-detail-report-pdf [context args value] (defn journal-detail-report-pdf [context args value]
(let [data (get-journal-detail-report context args value) (let [data (get-journal-detail-report context args value)
result (print-journal-detail-report (:id context) args data)] result (print-journal-detail-report (:id context) args data)]
(->graphql result))) (->graphql result)))
(def objects (def objects
{:balance_sheet_account {:balance_sheet_account
{:fields {:id {:type 'String} {:fields {:id {:type 'String}
@@ -881,8 +862,7 @@
:amount {:type :money} :amount {:type :money}
:note {:type 'String} :note {:type 'String}
:cleared_against {:type 'String} :cleared_against {:type 'String}
:line_items {:type '(list :import_ledger_line_item)}}} :line_items {:type '(list :import_ledger_line_item)}}}})
})
(def enums (def enums
{:ledger_category {:values [{:enum-value :sales} {:ledger_category {:values [{:enum-value :sales}
@@ -892,7 +872,6 @@
{:enum-value :fixed_overhead} {:enum-value :fixed_overhead}
{:enum-value :ownership_controllable}]}}) {:enum-value :ownership_controllable}]}})
(def resolvers (def resolvers
{:get-ledger-page get-ledger-page {:get-ledger-page get-ledger-page
:get-balance-sheet get-balance-sheet :get-balance-sheet get-balance-sheet

View File

@@ -21,13 +21,11 @@
:name (first name)})) :name (first name)}))
[])) []))
(defn attach [schema] (defn attach [schema]
(-> (->
(merge-with merge schema (merge-with merge schema
{:objects {:plaid_link_result {:objects {:plaid_link_result
{:fields {:token {:type 'String}} } {:fields {:token {:type 'String}}}
:plaid_item :plaid_item
{:fields {:external_id {:type 'String} {:fields {:external_id {:type 'String}

View File

@@ -1,6 +1,6 @@
(ns auto-ap.graphql.sales-orders (ns auto-ap.graphql.sales-orders
(:require [auto-ap.datomic.sales-orders :as d-sales-orders2] (:require [auto-ap.datomic.sales-orders :as d-sales-orders2]
[auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin] ] [auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin]]
[com.walmartlabs.lacinia.util :refer [attach-resolvers]] [com.walmartlabs.lacinia.util :refer [attach-resolvers]]
[auto-ap.graphql.utils :refer [attach-tracing-resolvers]])) [auto-ap.graphql.utils :refer [attach-tracing-resolvers]]))
@@ -17,7 +17,6 @@
->graphql ->graphql
(first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE))))) (first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
(def objects (def objects
{:sales_order_page {:sales_order_page
{:fields {:sales_orders {:type '(list :sales_order)} {:fields {:sales_orders {:type '(list :sales_order)}
@@ -93,8 +92,7 @@
(def resolvers (def resolvers
{:get-all-sales-orders get-all-sales-orders {:get-all-sales-orders get-all-sales-orders
:get-sales-order-page get-sales-orders-page :get-sales-order-page get-sales-orders-page})
})
(defn attach [schema] (defn attach [schema]
(-> (->

View File

@@ -29,9 +29,8 @@
(defn get-transaction-rule-matches [context args _] (defn get-transaction-rule-matches [context args _]
(if (= "admin" (:user/role (:id context))) (if (= "admin" (:user/role (:id context)))
(let [transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date) (let [transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)
all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction))) all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction)))]
]
(mu/log ::counted (mu/log ::counted
:count (count all-rules)) :count (count all-rules))
(doto (map ->graphql (rm/get-matching-rules transaction all-rules)) (#(println (count %))))) (doto (map ->graphql (rm/get-matching-rules transaction all-rules)) (#(println (count %)))))
@@ -43,7 +42,7 @@
:account account_id :account account_id
:location location}) :location location})
(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _] (defn delete-transaction-rule [context {:keys [transaction_rule_id]} _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)] (let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
(when-not (:transaction-rule/description existing-transaction-rule) (when-not (:transaction-rule/description existing-transaction-rule)
@@ -68,17 +67,16 @@
(throw (ex-info error {:validation-error error})))) (throw (ex-info error {:validation-error error}))))
_ (doseq [a accounts _ (doseq [a accounts
:let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account_id a)) :let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account_id a))
client (dc/pull (dc/db conn) [:client/locations] client_id) client (dc/pull (dc/db conn) [:client/locations] client_id)]]
]]
(when (and location (not= location (:location a))) (when (and location (not= location (:location a)))
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)] (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
(throw (ex-info err {:validation-error err}) ))) (throw (ex-info err {:validation-error err}))))
(when (and (not location) (when (and (not location)
(not (get (into #{"Shared"} (:client/locations client)) (not (get (into #{"Shared"} (:client/locations client))
(:location a)))) (:location a))))
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
(throw (ex-info err {:validation-error err}) )))) (throw (ex-info err {:validation-error err})))))
rule-id (if id rule-id (if id
id id
"transaction-rule") "transaction-rule")
@@ -100,7 +98,6 @@
(keyword "transaction-approval-status")) (keyword "transaction-approval-status"))
:transaction-rule/accounts (map transaction-rule-account->entity accounts)}]] :transaction-rule/accounts (map transaction-rule-account->entity accounts)}]]
transaction-result (audit-transact transaction (:id context))] transaction-result (audit-transact transaction (:id context))]
(-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule")) (-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule"))
id)) id))
@@ -110,9 +107,8 @@
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count] (defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
(let [query (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name] (let [query (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
:transaction/bank-account [:bank-account/name] :transaction/bank-account [:bank-account/name]
:transaction/payment [:db/id]} :transaction/payment [:db/id]}])]
])] :in ['$]
:in ['$ ]
:where []} :where []}
:args [(dc/db conn)]} :args [(dc/db conn)]}
description description
@@ -170,7 +166,6 @@
:where ['[?e :transaction/client ?client-id]]} :where ['[?e :transaction/client ?client-id]]}
:args [(:db/id client)]}) :args [(:db/id client)]})
(not include-coded?) (not include-coded?)
(merge-query {:query {:where ['[or [?e :transaction/approval-status :transaction-approval-status/unapproved] (merge-query {:query {:where ['[or [?e :transaction/approval-status :transaction-approval-status/unapproved]
[(missing? $ ?e :transaction/approval-status)]]]}}) [(missing? $ ?e :transaction/approval-status)]]]}})
@@ -200,7 +195,6 @@
:yodlee-merchant (when yodlee_merchant_id {:db/id yodlee_merchant_id})} :yodlee-merchant (when yodlee_merchant_id {:db/id yodlee_merchant_id})}
true 15)) true 15))
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _] (defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _]
(assert-admin id) (assert-admin id)
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count)) (-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))

View File

@@ -73,7 +73,7 @@
(<-graphql) (<-graphql)
(update :approval-status enum->keyword "transaction-approval-status") (update :approval-status enum->keyword "transaction-approval-status")
(assoc :per-page Integer/MAX_VALUE) (assoc :per-page Integer/MAX_VALUE)
(d-transactions/raw-graphql-ids ) (d-transactions/raw-graphql-ids)
:ids) :ids)
specific-ids (d-transactions/filter-ids (seq (:ids args)))] specific-ids (d-transactions/filter-ids (seq (:ids args)))]
(if (seq (:ids args)) (if (seq (:ids args))
@@ -100,8 +100,7 @@
(alog/info ::bulk-change-status (alog/info ::bulk-change-status
:count (count all-ids) :count (count all-ids)
:sample (take 3 all-ids) :sample (take 3 all-ids)
:status (:status args) :status (:status args))
)
(audit-transact-batch (audit-transact-batch
(->> all-ids (->> all-ids
(mapv (fn [t] (mapv (fn [t]
@@ -109,7 +108,7 @@
:transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}]))) :transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}])))
(:id context)) (:id context))
{:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args) ) ".")})) {:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args)) ".")}))
;; TODO very similar to rule-matching ;; TODO very similar to rule-matching
(defn maybe-code-accounts [transaction account-rules valid-locations] (defn maybe-code-accounts [transaction account-rules valid-locations]
@@ -154,7 +153,7 @@
{:validation-error "Client is required"}))) {:validation-error "Client is required"})))
(let [args (assoc args :clients (:clients context) :id (:id context)) (let [args (assoc args :clients (:clients context) :id (:id context))
client->locations (->> (:clients context) client->locations (->> (:clients context)
(map :db/id ) (map :db/id)
(dc/q (dc/q
'[:find (pull ?e [:db/id :client/locations]) '[:find (pull ?e [:db/id :client/locations])
:in $ [?e ...]] :in $ [?e ...]]
@@ -180,13 +179,13 @@
(:account_id a))]] (:account_id a))]]
(when (and location (not= location (:location a))) (when (and location (not= location (:location a)))
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)] (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
(throw (ex-info err {:validation-error err}) ))) (throw (ex-info err {:validation-error err}))))
(doseq [[_ locations] client->locations] (doseq [[_ locations] client->locations]
(when (and (not location) (when (and (not location)
(not (get (into #{"Shared"} locations) (not (get (into #{"Shared"} locations)
(:location a)))) (:location a))))
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
(throw (ex-info err {:validation-error err}) ))))) (throw (ex-info err {:validation-error err}))))))
(audit-transact-batch (audit-transact-batch
(map (fn [t] (map (fn [t]
(let [locations (client->locations (-> t :transaction/client :db/id))] (let [locations (client->locations (-> t :transaction/client :db/id))]
@@ -200,7 +199,6 @@
(:id context)) (:id context))
{:message (str "Successfully coded " (count all-ids) " transactions.")})) {:message (str "Successfully coded " (count all-ids) " transactions.")}))
(defn delete-transactions [context args _] (defn delete-transactions [context args _]
(let [_ (assert-admin (:id context)) (let [_ (assert-admin (:id context))
args (assoc args :clients (:clients context)) args (assoc args :clients (:clients context))
@@ -242,21 +240,21 @@
(assert-power-user (:id context)) (assert-power-user (:id context))
(let [transaction (d-transactions/get-by-id (:transaction_id args)) (let [transaction (d-transactions/get-by-id (:transaction_id args))
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
matches-set (i-transactions/match-transaction-to-unfulfilled-autopayments (:transaction/amount transaction) matches-set (i-transactions/match-transaction-to-unfulfilled-autopayments (:transaction/amount transaction)
(:db/id (:transaction/client transaction)))] (:db/id (:transaction/client transaction)))]
(->graphql (for [matches matches-set] (->graphql (for [matches matches-set]
(for [[_ invoice-id ] matches] (for [[_ invoice-id] matches]
(d-invoices/get-by-id invoice-id)))))) (d-invoices/get-by-id invoice-id))))))
(defn get-potential-unpaid-invoices-matches [context args _] (defn get-potential-unpaid-invoices-matches [context args _]
(assert-power-user (:id context)) (assert-power-user (:id context))
(let [transaction (d-transactions/get-by-id (:transaction_id args)) (let [transaction (d-transactions/get-by-id (:transaction_id args))
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
matches-set (i-transactions/match-transaction-to-unpaid-invoices (:transaction/amount transaction) matches-set (i-transactions/match-transaction-to-unpaid-invoices (:transaction/amount transaction)
(:db/id (:transaction/client transaction)))] (:db/id (:transaction/client transaction)))]
(->graphql (for [matches matches-set] (->graphql (for [matches matches-set]
(for [[_ invoice-id ] matches] (for [[_ invoice-id] matches]
(d-invoices/get-by-id invoice-id)))))) (d-invoices/get-by-id invoice-id))))))
(defn unlink-transaction [context args _] (defn unlink-transaction [context args _]
@@ -271,13 +269,13 @@
:transaction/vendor :transaction/vendor
:transaction/accounts :transaction/accounts
:transaction/client [:db/id] :transaction/client [:db/id]
{:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]} ] {:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]}]
transaction-id) transaction-id)
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction)) _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
_ (when (:transaction/payment transaction) _ (when (:transaction/payment transaction)
(assert-not-locked (:db/id (:transaction/client transaction)) (-> transaction :transaction/payment :payment/date))) (assert-not-locked (:db/id (:transaction/client transaction)) (-> transaction :transaction/payment :payment/date)))
payment (-> transaction :transaction/payment ) payment (-> transaction :transaction/payment)
is-autopay-payment? (some->> (dc/q {:find ['?sp] is-autopay-payment? (some->> (dc/q {:find ['?sp]
:in ['$ '?payment] :in ['$ '?payment]
:where ['[?ip :invoice-payment/payment ?payment] :where ['[?ip :invoice-payment/payment ?payment]
@@ -286,8 +284,7 @@
(dc/db conn) (:db/id payment)) (dc/db conn) (:db/id payment))
seq seq
(map first) (map first)
(every? #(instance? java.util.Date %))) (every? #(instance? java.util.Date %)))]
]
(alog/info ::unlinking :transaction (pr-str transaction) :autopay is-autopay-payment? :payment (pr-str payment)) (alog/info ::unlinking :transaction (pr-str transaction) :autopay is-autopay-payment? :payment (pr-str payment))
@@ -305,7 +302,7 @@
:transaction/location nil :transaction/location nil
:transaction/accounts nil}] :transaction/accounts nil}]
[:db/retractEntity (:db/id payment) ]] [:db/retractEntity (:db/id payment)]]
(into (map (fn [[invoice-payment]] (into (map (fn [[invoice-payment]]
[:db/retractEntity invoice-payment]) [:db/retractEntity invoice-payment])
@@ -313,7 +310,7 @@
:in ['$ '?p] :in ['$ '?p]
:where ['[?ip :invoice-payment/payment ?p]]} :where ['[?ip :invoice-payment/payment ?p]]}
(dc/db conn) (dc/db conn)
(:db/id payment) )))) (:db/id payment)))))
(:id context)) (:id context))
(audit-transact (audit-transact
[{:db/id (:db/id payment) [{:db/id (:db/id payment)
@@ -330,14 +327,12 @@
approval-status->graphql approval-status->graphql
->graphql))) ->graphql)))
(defn transaction-account->entity [{:keys [id account_id amount location]}] (defn transaction-account->entity [{:keys [id account_id amount location]}]
#:transaction-account {:amount amount #:transaction-account {:amount amount
:db/id (or id (random-tempid)) :db/id (or id (random-tempid))
:account account_id :account account_id
:location location}) :location location})
(defn assert-valid-expense-accounts [accounts] (defn assert-valid-expense-accounts [accounts]
(doseq [trans-account accounts (doseq [trans-account accounts
:let [account (dc/pull (dc/db conn) :let [account (dc/pull (dc/db conn)
@@ -359,13 +354,12 @@
(throw (ex-info err (throw (ex-info err
{:validation-error err})))) {:validation-error err}))))
(when (nil? (:account_id trans-account)) (when (nil? (:account_id trans-account))
(throw (ex-info "Account is missing account" {:validation-error "Account is missing account"}))))) (throw (ex-info "Account is missing account" {:validation-error "Account is missing account"})))))
(defn edit-transaction [context {{:keys [id accounts vendor_id approval_status memo forecast_match]} :transaction} _] (defn edit-transaction [context {{:keys [id accounts vendor_id approval_status memo forecast_match]} :transaction} _]
(let [existing-transaction (d-transactions/get-by-id id) (let [existing-transaction (d-transactions/get-by-id id)
_ (assert-can-see-client (:id context) (:transaction/client existing-transaction) ) _ (assert-can-see-client (:id context) (:transaction/client existing-transaction))
_ (assert-valid-expense-accounts accounts) _ (assert-valid-expense-accounts accounts)
_ (assert-not-locked (:db/id (:transaction/client existing-transaction)) (:transaction/date existing-transaction)) _ (assert-not-locked (:db/id (:transaction/client existing-transaction)) (:transaction/date existing-transaction))
account-total (reduce + 0 (map (fn [x] (:amount x)) accounts)) account-total (reduce + 0 (map (fn [x] (:amount x)) accounts))
@@ -387,7 +381,7 @@
(let [error (str "Expense account total (" account-total ") does not equal transaction total (" (Math/abs (:transaction/amount existing-transaction)) ")")] (let [error (str "Expense account total (" account-total ") does not equal transaction total (" (Math/abs (:transaction/amount existing-transaction)) ")")]
(throw (ex-info error {:validation-error error})))) (throw (ex-info error {:validation-error error}))))
(when missing-locations (when missing-locations
(throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})) ) (throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})))
(audit-transact (cond-> [[:upsert-transaction {:db/id id (audit-transact (cond-> [[:upsert-transaction {:db/id id
:transaction/vendor vendor_id :transaction/vendor vendor_id
@@ -413,8 +407,8 @@
(defn match-transaction [context {:keys [transaction_id payment_id]} _] (defn match-transaction [context {:keys [transaction_id payment_id]} _]
(let [transaction (d-transactions/get-by-id transaction_id) (let [transaction (d-transactions/get-by-id transaction_id)
payment (d-checks/get-by-id payment_id) payment (d-checks/get-by-id payment_id)
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
_ (assert-can-see-client (:id context) (:payment/client payment) ) _ (assert-can-see-client (:id context) (:payment/client payment))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))] _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))]
(when (not= (:db/id (:transaction/client transaction)) (when (not= (:db/id (:transaction/client transaction))
(:db/id (:payment/client payment))) (:db/id (:payment/client payment)))
@@ -448,7 +442,7 @@
(defn match-transaction-autopay-invoices [context {:keys [transaction_id autopay_invoice_ids]} _] (defn match-transaction-autopay-invoices [context {:keys [transaction_id autopay_invoice_ids]} _]
(let [_ (assert-power-user (:id context)) (let [_ (assert-power-user (:id context))
transaction (d-transactions/get-by-id transaction_id) transaction (d-transactions/get-by-id transaction_id)
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
db (dc/db conn) db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) autopay_invoice_ids)) invoice-clients (set (map #(pull-ref db :invoice/client %) autopay_invoice_ids))
invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay_invoice_ids)) invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay_invoice_ids))
@@ -486,7 +480,7 @@
(let [_ (assert-power-user (:id context)) (let [_ (assert-power-user (:id context))
transaction (d-transactions/get-by-id transaction_id) transaction (d-transactions/get-by-id transaction_id)
_ (assert-can-see-client (:id context) (:transaction/client transaction) ) _ (assert-can-see-client (:id context) (:transaction/client transaction))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction)) _ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
db (dc/db conn) db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid_invoice_ids)) invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid_invoice_ids))
@@ -527,8 +521,7 @@
:count Integer/MAX_VALUE} nil) :count Integer/MAX_VALUE} nil)
(filter #(not (:payment %))) (filter #(not (:payment %)))
(map :id )) (map :id))
transaction_ids) transaction_ids)
_ (mu/log ::here :txids transaction_ids) _ (mu/log ::here :txids transaction_ids)
@@ -562,8 +555,7 @@
(:id context)) (:id context))
(doseq [n transactions] (doseq [n transactions]
(solr/touch-with-ledger (:db/id n))) (solr/touch-with-ledger (:db/id n))))
)
(transduce (transduce
(comp (comp
(map d-transactions/get-by-id) (map d-transactions/get-by-id)
@@ -571,7 +563,7 @@
(map ->graphql)) (map ->graphql))
conj conj
[] []
transaction_ids )) transaction_ids))
(def objects (def objects
{:transaction {:fields {:id {:type :id} {:transaction {:fields {:id {:type :id}
@@ -711,7 +703,6 @@
:mutation/match-transaction-unpaid-invoices match-transaction-unpaid-invoices :mutation/match-transaction-unpaid-invoices match-transaction-unpaid-invoices
:mutation/match-transaction-rules match-transaction-rules}) :mutation/match-transaction-rules match-transaction-rules})
(defn attach [schema] (defn attach [schema]
(-> (->
(merge-with merge schema (merge-with merge schema

View File

@@ -13,7 +13,6 @@
[iol-ion.query :refer [entid]] [iol-ion.query :refer [entid]]
[slingshot.slingshot :refer [throw+]])) [slingshot.slingshot :refer [throw+]]))
(defn snake->kebab [s] (defn snake->kebab [s]
(str/replace s #"_" "-")) (str/replace s #"_" "-"))
@@ -107,8 +106,6 @@
(#{"manager" "user" "power-user" "read-only"} (:user/role id)) (#{"manager" "user" "power-user" "read-only"} (:user/role id))
(:user/clients id []))) (:user/clients id [])))
(defn result->page [results result-count key args] (defn result->page [results result-count key args]
{key (map ->graphql results) {key (map ->graphql results)
:total result-count :total result-count
@@ -197,7 +194,6 @@
(= :client/code (first x))) (= :client/code (first x)))
[(entid (dc/db conn) x)] [(entid (dc/db conn) x)]
(sequential? x) (sequential? x)
(mapcat coerce-client-ids x) (mapcat coerce-client-ids x)
@@ -221,7 +217,7 @@
(try (try
(f) (f)
(catch Throwable e (catch Throwable e
(throw+ (ex-info (.getMessage e) {:type :form-validation (throw+ (ex-info (.getMessage e) {:type :form-validation
:form-validation-errors [(.getMessage e)]})) :form-validation-errors [(.getMessage e)]}))
#_(throw (ex-info (.getMessage e) #_(throw (ex-info (.getMessage e)
{:type :notification} {:type :notification}

View File

@@ -130,7 +130,6 @@
:vendor/schedule-payment-dom schedule-payment-dom :vendor/schedule-payment-dom schedule-payment-dom
:vendor/automatically-paid-when-due (:automatically_paid_when_due in)))] :vendor/automatically-paid-when-due (:automatically_paid_when_due in)))]
transaction-result (audit-transact [transaction] (:id context)) transaction-result (audit-transact [transaction] (:id context))
new-vendor (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor")) new-vendor (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor"))
id))] id))]
@@ -160,7 +159,6 @@
(audit-transact transaction (:id context)) (audit-transact transaction (:id context))
to)) to))
(defn get-graphql [context args _] (defn get-graphql [context args _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))
@@ -187,7 +185,6 @@
(if-let [query (not-empty (cleanse-query (:query args)))] (if-let [query (not-empty (cleanse-query (:query args)))]
(let [search-query (str "name:(" query ")")] (let [search-query (str "name:(" query ")")]
(for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query (for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query
(not (is-admin? (:id context))) (str " hidden:false")) (not (is-admin? (:id context))) (str " hidden:false"))
"fields" "id, name"})] "fields" "id, name"})]

View File

@@ -70,7 +70,6 @@
:headers {} :headers {}
:body ""}) :body ""})
(defn home-handler [{:keys [identity]}] (defn home-handler [{:keys [identity]}]
(if identity (if identity
{:status 302 {:status 302
@@ -78,7 +77,6 @@
{:status 302 {:status 302
:headers {"Location" "/login"}})) :headers {"Location" "/login"}}))
(def match->handler-lookup (def match->handler-lookup
(-> {:not-found not-found (-> {:not-found not-found
:home home-handler} :home home-handler}
@@ -90,15 +88,13 @@
(merge yodlee2/match->handler) (merge yodlee2/match->handler)
(merge auth/match->handler) (merge auth/match->handler)
(merge invoices/match->handler) (merge invoices/match->handler)
(merge exports/match->handler) (merge exports/match->handler)))
))
(def match->handler (def match->handler
(fn [route] (fn [route]
(or (get match->handler-lookup route) (or (get match->handler-lookup route)
route))) route)))
(def route-handler (def route-handler
(make-handler all-routes (make-handler all-routes
match->handler)) match->handler))
@@ -125,7 +121,6 @@
uri uri
:request-method method)) :request-method method))
(def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}})) (def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}}))
(defn wrap-logging [handler] (defn wrap-logging [handler]
@@ -159,8 +154,6 @@
:exception e) :exception e)
(throw e))))))) (throw e)))))))
(defn wrap-idle-session-timeout (defn wrap-idle-session-timeout
[handler] [handler]
(fn [request] (fn [request]

View File

@@ -13,7 +13,7 @@
[iol-ion.utils :refer [remove-nils]])) [iol-ion.utils :refer [remove-nils]]))
(defn get-intuit-bank-accounts (defn get-intuit-bank-accounts
( [db] ([db]
(dc/q '[:find ?external-id ?ba ?c (dc/q '[:find ?external-id ?ba ?c
:in $ :in $
:where :where
@@ -32,7 +32,6 @@
db db
client-codes))) client-codes)))
(defn intuit->transaction [transaction] (defn intuit->transaction [transaction]
(let [check-number (when (not (str/blank? (:Num transaction))) (let [check-number (when (not (str/blank? (:Num transaction)))
(try (try
@@ -46,7 +45,6 @@
:transaction/status "POSTED"} :transaction/status "POSTED"}
check-number (assoc :transaction/check-number check-number)))) check-number (assoc :transaction/check-number check-number))))
(defn intuits->transactions [transactions bank-account-id client-id] (defn intuits->transactions [transactions bank-account-id client-id]
(->> transactions (->> transactions
(map intuit->transaction) (map intuit->transaction)

View File

@@ -11,7 +11,6 @@
(t/is (= #inst "2021-01-01T00:00:00-08:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-01-01"))))) (t/is (= #inst "2021-01-01T00:00:00-08:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-01-01")))))
(t/is (= #inst "2021-06-01T00:00:00-07:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-06-01"))))))) (t/is (= #inst "2021-06-01T00:00:00-07:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-06-01")))))))
(t/deftest intuits->transactions (t/deftest intuits->transactions
(t/testing "should give unique ids to duplicates" (t/testing "should give unique ids to duplicates"
(t/is (= ["2021-10-11T00:00:00.000-07:00-123-this is a description-45.23-0-345" (t/is (= ["2021-10-11T00:00:00.000-07:00-123-this is a description-45.23-0-345"

View File

@@ -7,8 +7,6 @@
[clojure.data.csv :as csv] [clojure.data.csv :as csv]
[datomic.api :as dc])) [datomic.api :as dc]))
(def columns [:status :raw-date :description-original :high-level-category nil nil :amount nil nil nil nil nil :bank-account-code :client-code]) (def columns [:status :raw-date :description-original :high-level-category nil nil :amount nil nil nil nil nil :bank-account-code :client-code])
(defn tabulate-data [data] (defn tabulate-data [data]
@@ -48,7 +46,7 @@
transactions (->> transactions transactions (->> transactions
(map (fn [t] (map (fn [t]
(manual->transaction t bank-account-code->bank-account bank-account-code->client))) (manual->transaction t bank-account-code->bank-account bank-account-code->client)))
(t/apply-synthetic-ids ))] (t/apply-synthetic-ids))]
(try (try
(doseq [transaction transactions] (doseq [transaction transactions]
(when-not (seq (:errors transaction)) (when-not (seq (:errors transaction))

View File

@@ -40,7 +40,6 @@
db db
client-codes)))) client-codes))))
(defn plaid->transaction [t plaid-merchant->vendor-id] (defn plaid->transaction [t plaid-merchant->vendor-id]
(alog/info ::trying-transaction :transaction t) (alog/info ::trying-transaction :transaction t)
(cond-> #:transaction {:description-original (:name t) (cond-> #:transaction {:description-original (:name t)
@@ -66,8 +65,7 @@
:where :where
[?v :vendor/plaid-merchant ?pm] [?v :vendor/plaid-merchant ?pm]
[?pm :plaid-merchant/name ?pmn]] [?pm :plaid-merchant/name ?pmn]]
(dc/db conn )))) (dc/db conn))))
(def single-thread (ex/fixed-thread-executor 1)) (def single-thread (ex/fixed-thread-executor 1))
@@ -103,13 +101,9 @@
(catch Exception e (catch Exception e
(alog/warn ::couldnt-upsert-account :error e)))) (alog/warn ::couldnt-upsert-account :error e))))
(catch Exception e (catch Exception e
(alog/warn ::couldnt-upsert-accounts :error e)))) (alog/warn ::couldnt-upsert-accounts :error e))))
(defn import-plaid-int [] (defn import-plaid-int []
(let [_ (upsert-accounts) (let [_ (upsert-accounts)
import-batch (t/start-import-batch :import-source/plaid "Automated plaid user") import-batch (t/start-import-batch :import-source/plaid "Automated plaid user")

View File

@@ -29,7 +29,6 @@
nil)) nil))
nil)) nil))
(defn transaction->existing-payment [_ check-number client-id bank-account-id amount id] (defn transaction->existing-payment [_ check-number client-id bank-account-id amount id]
(alog/info ::searching (alog/info ::searching
:client-id client-id :client-id client-id
@@ -169,12 +168,10 @@
(= 1234 (extract-check-number {:transaction/description-original "Check abc 1234"})) (= 1234 (extract-check-number {:transaction/description-original "Check abc 1234"}))
(= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234"})) (= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234"}))
(= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234 12/3"})) (= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234 12/3"}))
(not= 1234 (extract-check-number {:transaction/description-original "Checkcard 4/10 1234"})) (not= 1234 (extract-check-number {:transaction/description-original "Checkcard 4/10 1234"})))
)
(defn find-expected-deposit [client-id amount date] (defn find-expected-deposit [client-id amount date]
(when date (when date
@@ -187,12 +184,10 @@
[?ed :expected-deposit/date ?d] [?ed :expected-deposit/date ?d]
[(>= ?d ?d-start)] [(>= ?d ?d-start)]
[?ed :expected-deposit/total ?a2] [?ed :expected-deposit/total ?a2]
[(auto-ap.utils/dollars= ?a2 ?a)] [(auto-ap.utils/dollars= ?a2 ?a)]]
]
(dc/db conn) client-id amount (coerce/to-date (t/plus date (t/days -10)))) (dc/db conn) client-id amount (coerce/to-date (t/plus date (t/days -10))))
ffirst))) ffirst)))
(defn categorize-transaction [transaction bank-account existing] (defn categorize-transaction [transaction bank-account existing]
(cond (= :transaction-approval-status/suppressed (existing (:transaction/id transaction))) (cond (= :transaction-approval-status/suppressed (existing (:transaction/id transaction)))
:suppressed :suppressed
@@ -235,7 +230,6 @@
(assoc transaction :transaction/check-number check-number) (assoc transaction :transaction/check-number check-number)
transaction)) transaction))
(defn maybe-clear-payment [{:transaction/keys [check-number client bank-account amount id] :as transaction}] (defn maybe-clear-payment [{:transaction/keys [check-number client bank-account amount id] :as transaction}]
(when-let [existing-payment (transaction->existing-payment transaction check-number client bank-account amount id)] (when-let [existing-payment (transaction->existing-payment transaction check-number client bank-account amount id)]
(assoc transaction (assoc transaction
@@ -266,8 +260,7 @@
:transaction-account/amount amount :transaction-account/amount amount
:transaction-account/location "A"}] :transaction-account/location "A"}]
:transaction/approval-status :transaction-approval-status/approved :transaction/approval-status :transaction-approval-status/approved
:transaction/vendor (:db/id (:expected-deposit/vendor expected-deposit)) :transaction/vendor (:db/id (:expected-deposit/vendor expected-deposit))))))
))))
(defn maybe-code [{:transaction/keys [client amount] :as transaction} apply-rules valid-locations] (defn maybe-code [{:transaction/keys [client amount] :as transaction} apply-rules valid-locations]
(mu/trace (mu/trace
@@ -304,7 +297,6 @@
(maybe-apply-default-vendor) (maybe-apply-default-vendor)
remove-nils))) remove-nils)))
(defn get-existing [bank-account] (defn get-existing [bank-account]
(into {} (into {}
(dc/q '[:find ?tid ?as2 (dc/q '[:find ?tid ?as2
@@ -317,7 +309,7 @@
(defprotocol ImportBatch (defprotocol ImportBatch
(import-transaction! [this transaction]) (import-transaction! [this transaction])
(get-stats [this ]) (get-stats [this])
(get-pending-balance [this bank-account]) (get-pending-balance [this bank-account])
(finish! [this]) (finish! [this])
(fail! [this error])) (fail! [this error]))
@@ -326,7 +318,7 @@
:db/id :db/id
:bank-account/locations :bank-account/locations
:bank-account/start-date :bank-account/start-date
{:client/_bank-accounts [:client/code :client/locked-until :client/locations :db/id]} ]) {:client/_bank-accounts [:client/code :client/locked-until :client/locations :db/id]}])
(defn start-import-batch [source user] (defn start-import-batch [source user]
(let [stats (atom {:import-batch/imported 0 (let [stats (atom {:import-batch/imported 0
@@ -335,7 +327,7 @@
:import-batch/not-ready 0 :import-batch/not-ready 0
:import-batch/extant 0}) :import-batch/extant 0})
pending-balance (atom {}) pending-balance (atom {})
extant-cache (atom (cache/ttl-cache-factory {} :ttl 60000 )) extant-cache (atom (cache/ttl-cache-factory {} :ttl 60000))
import-id (get (:tempids @(dc/transact-async conn [{:db/id "import-batch" import-id (get (:tempids @(dc/transact-async conn [{:db/id "import-batch"
:import-batch/date (coerce/to-date (t/now)) :import-batch/date (coerce/to-date (t/now))
:import-batch/source source :import-batch/source source
@@ -410,9 +402,7 @@
:import-batch/status :import-status/completed} :import-batch/status :import-status/completed}
@stats)]))))) @stats)])))))
(defn synthetic-key [{:transaction/keys [date bank-account description-original amount client]} index]
(defn synthetic-key [{:transaction/keys [date bank-account description-original amount client] } index]
(str (str (some-> date coerce/to-date-time atime/localize)) "-" bank-account "-" description-original "-" amount "-" index "-" client)) (str (str (some-> date coerce/to-date-time atime/localize)) "-" bank-account "-" description-original "-" amount "-" index "-" client))
(defn apply-synthetic-ids [transactions] (defn apply-synthetic-ids [transactions]

View File

@@ -95,5 +95,4 @@
nil) nil)
(Thread/sleep 10000))))) (Thread/sleep 10000)))))
(def import-yodlee2 (allow-once import-yodlee2-int)) (def import-yodlee2 (allow-once import-yodlee2-int))

View File

@@ -15,7 +15,6 @@
;; (def base-url "https://sandbox-quickbooks.api.intuit.com/v3") ;; (def base-url "https://sandbox-quickbooks.api.intuit.com/v3")
(def prod-client-id "ABFRwAiOqQiLN66HKplXfyRE3ipD390DHsrUquflRCiOa81mxa") (def prod-client-id "ABFRwAiOqQiLN66HKplXfyRE3ipD390DHsrUquflRCiOa81mxa")
(def prod-client-secret "xDUj04GeQXpLvrhxep1jjDYwjJWbzzOPrirUQTKF") (def prod-client-secret "xDUj04GeQXpLvrhxep1jjDYwjJWbzzOPrirUQTKF")
@@ -27,11 +26,8 @@
;; "accessToken":, ;; "accessToken":,
;; ;;
(def prod-company-id "123146163906404") (def prod-company-id "123146163906404")
(def prod-base-url "https://quickbooks.api.intuit.com/v3") (def prod-base-url "https://quickbooks.api.intuit.com/v3")
(defn set-access-token [t] (defn set-access-token [t]
@@ -53,7 +49,6 @@
:bucket-name "data.prod.app.integreatconsult.com" :bucket-name "data.prod.app.integreatconsult.com"
:key "intuit/refresh-token"))))) :key "intuit/refresh-token")))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn init-tokens [access refresh] (defn init-tokens [access refresh]
(set-access-token access) (set-access-token access)
@@ -74,10 +69,9 @@
(defn get-basic-auth [] (defn get-basic-auth []
(Base64/encodeBase64String (.getBytes (str prod-client-id ":" prod-client-secret)))) (Base64/encodeBase64String (.getBytes (str prod-client-id ":" prod-client-secret))))
(defn get-fresh-access-token [] (defn get-fresh-access-token []
(let [refresh-token (lookup-refresh-token) (let [refresh-token (lookup-refresh-token)
response (:body (client/post (str "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer" ) response (:body (client/post (str "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer")
{:headers {"Accept" "application/json" {:headers {"Accept" "application/json"
"Content-Type" "application/x-www-form-urlencoded" "Content-Type" "application/x-www-form-urlencoded"
@@ -99,16 +93,15 @@
:as :json}) :as :json})
(defn get-bank-accounts-raw [token] (defn get-bank-accounts-raw [token]
(->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query" ) (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query")
{:headers {:headers
(with-auth prod-base-headers token) (with-auth prod-base-headers token)
:as :json :as :json
:query-params {"query" "SELECT * From Account maxresults 1000"}})) :query-params {"query" "SELECT * From Account maxresults 1000"}}))
:QueryResponse)) :QueryResponse))
(defn get-bank-accounts [token] (defn get-bank-accounts [token]
(->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query" ) (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query")
{:headers {:headers
(with-auth prod-base-headers token) (with-auth prod-base-headers token)
:as :json :as :json
@@ -124,7 +117,6 @@
:last-updated (c/to-date-time (-> metadata :LastUpdatedTime)) :last-updated (c/to-date-time (-> metadata :LastUpdatedTime))
:current-balance (try (double current-balance) (catch Exception _ nil))})))) :current-balance (try (double current-balance) (catch Exception _ nil))}))))
(defn get-all-transactions [start end] (defn get-all-transactions [start end]
(let [token (get-fresh-access-token)] (let [token (get-fresh-access-token)]
(:body (client/get (str prod-base-url "/company/" prod-company-id "/reports/TransactionList" "?minorversion=63&start_date=" start "&end_date=" end) (:body (client/get (str prod-base-url "/company/" prod-company-id "/reports/TransactionList" "?minorversion=63&start_date=" start "&end_date=" end)

View File

@@ -12,7 +12,6 @@
(defn line->id [{:keys [source id client-code]}] (defn line->id [{:keys [source id client-code]}]
(str client-code "-" source "-" id)) (str client-code "-" source "-" id))
(defn csv->graphql-rows [lines] (defn csv->graphql-rows [lines]
(for [lines (partition-by line->id (drop 1 lines)) (for [lines (partition-by line->id (drop 1 lines))
:let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]] :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]

View File

@@ -20,8 +20,7 @@
(mapv (fn [[i]] {:db/id i (mapv (fn [[i]] {:db/id i
:invoice/outstanding-balance 0.0 :invoice/outstanding-balance 0.0
:invoice/status :invoice-status/paid})) :invoice/status :invoice-status/paid}))))
))
(alog/info ::closed :count (count invoices-to-close)))) (alog/info ::closed :count (count invoices-to-close))))

View File

@@ -1,7 +1,7 @@
(ns auto-ap.jobs.core (ns auto-ap.jobs.core
(:require [auto-ap.utils :refer [heartbeat]] (:require [auto-ap.utils :refer [heartbeat]]
[mount.core :as mount] [mount.core :as mount]
[auto-ap.datomic :refer [conn ]] [auto-ap.datomic :refer [conn]]
[auto-ap.logging :as alog] [auto-ap.logging :as alog]
[nrepl.server :refer [start-server]] [nrepl.server :refer [start-server]]
[auto-ap.background.metrics :refer [metrics-setup container-tags container-data logging-context]] [auto-ap.background.metrics :refer [metrics-setup container-tags container-data logging-context]]
@@ -14,7 +14,7 @@
(mu/trace ::execute-background-job (mu/trace ::execute-background-job
[] []
(try (try
(mount/start (mount/only #{#'conn #'metrics-setup #'container-tags #'logging-context #'container-data })) (mount/start (mount/only #{#'conn #'metrics-setup #'container-tags #'logging-context #'container-data}))
(start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler)) (start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler))
((heartbeat f name)) ((heartbeat f name))
(alog/info ::stopping :job name) (alog/info ::stopping :job name)

View File

@@ -29,7 +29,6 @@
:body {:html (str "<div>You can download the original email <a href=\"" target-url "\">here</a>.<p><pre>" message "</pre></p></div>") :body {:html (str "<div>You can download the original email <a href=\"" target-url "\">here</a>.<p><pre>" message "</pre></p></div>")
:text (str "<div>You can download the original email here: " target-url)}}}))) :text (str "<div>You can download the original email here: " target-url)}}})))
(defn process-sqs [] (defn process-sqs []
(alog/info ::fetching-sqs) (alog/info ::fetching-sqs)
(doseq [message (:messages (sqs/receive-message {:queue-url (:invoice-import-queue-url env) (doseq [message (:messages (sqs/receive-message {:queue-url (:invoice-import-queue-url env)
@@ -79,27 +78,20 @@
(defn -main [& _] (defn -main [& _]
(execute "import-uploaded-invoices" process-sqs)) (execute "import-uploaded-invoices" process-sqs))
(comment (comment
(with-open [i (io/output-stream "/tmp/bryce.pdf")] (with-open [i (io/output-stream "/tmp/bryce.pdf")]
(clojure.java.io/copy (clojure.java.io/copy
(-> (s3/get-object :bucket-name (:data-bucket env) (-> (s3/get-object :bucket-name (:data-bucket env)
:key "invoice-files/f0e73dcb-e5e5-4c81-b82b-319b7caedacf.pdf" :key "invoice-files/f0e73dcb-e5e5-4c81-b82b-319b7caedacf.pdf")
)
:input-stream) :input-stream)
i)) i))
(parse/parse-file "/tmp/bryce.pdf" "/tmp/bryce.pdf") (parse/parse-file "/tmp/bryce.pdf" "/tmp/bryce.pdf")
(-> (clojure.java.shell/sh "pdftotext" "-layout" "/tmp/bryce.pdf" "-") (-> (clojure.java.shell/sh "pdftotext" "-layout" "/tmp/bryce.pdf" "-")
:out :out)
)
1 1
(user/init-repl) (user/init-repl))
)

View File

@@ -10,13 +10,12 @@
[config.core :refer [env]] [config.core :refer [env]]
[datomic.api :as dc])) [datomic.api :as dc]))
(defn historical-load-sales [client days] (defn historical-load-sales [client days]
(alog/info ::new-sales-loading :client (:client/code client) :days days) (alog/info ::new-sales-loading :client (:client/code client) :days days)
(let [client (dc/pull (dc/db auto-ap.datomic/conn) (let [client (dc/pull (dc/db auto-ap.datomic/conn)
square3/square-read square3/square-read
client) client)
days (cond-> days (string? days) ( #(Long/parseLong %)))] days (cond-> days (string? days) (#(Long/parseLong %)))]
(doseq [square-location (:client/square-locations client) (doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)] :when (:square-location/client-location square-location)]
@@ -31,11 +30,10 @@
@(square3/upsert-refunds client square-location) @(square3/upsert-refunds client square-location)
@(square3/upsert-payouts client square-location (time/plus (time/now) (time/days (- days))) (time/now))))) @(square3/upsert-payouts client square-location (time/plus (time/now) (time/days (- days))) (time/now)))))
(defn load-historical-sales [args] (defn load-historical-sales [args]
(let [{:keys [days client]} args (let [{:keys [days client]} args
client (cond-> client client (cond-> client
( string? client) ( #( Long/parseLong %)))] (string? client) (#(Long/parseLong %)))]
(historical-load-sales client days))) (historical-load-sales client days)))
(defn -main [& _] (defn -main [& _]

View File

@@ -28,12 +28,10 @@
(defn read-xml [stream] (defn read-xml [stream]
(-> (slurp stream) (-> (slurp stream)
(.getBytes) (.getBytes)
(java.io.ByteArrayInputStream. ) (java.io.ByteArrayInputStream.)
xml/parse xml/parse
zip/xml-zip)) zip/xml-zip))
(defn mark-key [k] (defn mark-key [k]
(s3/copy-object {:source-bucket-name bucket-name (s3/copy-object {:source-bucket-name bucket-name
:destination-bucket-name bucket-name :destination-bucket-name bucket-name
@@ -71,10 +69,10 @@
(drop 1) (drop 1)
#_(filter (fn [[_ _ _ _ _ _ _ _ _ _ _ break-flag]] #_(filter (fn [[_ _ _ _ _ _ _ _ _ _ _ break-flag]]
(= "Y" break-flag))) (= "Y" break-flag)))
(map (fn [[_ location-hint invoice-number ship-date invoice-total ]] (map (fn [[_ location-hint invoice-number ship-date invoice-total]]
(let [matching-client (and location-hint (let [matching-client (and location-hint
(parse/exact-match clients location-hint)) (parse/exact-match clients location-hint))
location (parse/best-location-match matching-client location-hint location-hint ) location (parse/best-location-match matching-client location-hint location-hint)
vendor (d/pull (d/db conn) '[:vendor/default-account] :vendor/general-produce)] vendor (d/pull (d/db conn) '[:vendor/default-account] :vendor/general-produce)]
(when-not (and matching-client (when-not (and matching-client
(not (@missing-client-hints location-hint)) (not (@missing-client-hints location-hint))
@@ -99,8 +97,7 @@
(-> vendor :vendor/default-account :db/id) (-> vendor :vendor/default-account :db/id)
:invoice-expense-account/location location :invoice-expense-account/location location
:invoice-expense-account/amount (Math/abs (Double/parseDouble invoice-total)) :invoice-expense-account/amount (Math/abs (Double/parseDouble invoice-total))
:db/id (random-tempid) :db/id (random-tempid)}]})))
}]})))
(filter :invoice/client) (filter :invoice/client)
(reduce (fn [[seen-so-far list] i] (reduce (fn [[seen-so-far list] i]
(let [k [(:invoice/invoice-number i) (:invoice/client i)]] (let [k [(:invoice/invoice-number i) (:invoice/client i)]]
@@ -108,8 +105,7 @@
[seen-so-far list] [seen-so-far list]
[(conj seen-so-far k) (conj list i)]))) [(conj seen-so-far k) (conj list i)])))
[#{} []]) [#{} []])
(second) (second))
)
(catch Exception e (catch Exception e
(log/error ::cant-import-general-produce (log/error ::cant-import-general-produce
:error e) :error e)
@@ -123,7 +119,7 @@
(defn zip-seq [zipper] (defn zip-seq [zipper]
(->> (zip/xml-zip (zip/node zipper)) (->> (zip/xml-zip (zip/node zipper))
(iterate zip/next ) (iterate zip/next)
(take-while (complement zip/end?)))) (take-while (complement zip/end?))))
(defmethod extract-invoice-details :cintas (defmethod extract-invoice-details :cintas
@@ -160,7 +156,7 @@
atime/localize atime/localize
(atime/unparse atime/iso-date) (atime/unparse atime/iso-date)
(atime/parse atime/iso-date)))) (atime/parse atime/iso-date))))
location (parse/best-location-match matching-client location-hint location-hint ) location (parse/best-location-match matching-client location-hint location-hint)
due (-> invoice-date due (-> invoice-date
(time/plus (time/days 30)) (time/plus (time/days 30))
(coerce/to-date)) (coerce/to-date))
@@ -178,7 +174,7 @@
:content :content
first first
Double/parseDouble) Double/parseDouble)
invoice {:db/id (random-tempid ) invoice {:db/id (random-tempid)
:invoice/vendor :vendor/cintas :invoice/vendor :vendor/cintas
:invoice/import-status :import-status/imported :invoice/import-status :import-status/imported
:invoice/status :invoice-status/unpaid :invoice/status :invoice-status/unpaid
@@ -206,8 +202,7 @@
(-> vendor :vendor/default-account :db/id) (-> vendor :vendor/default-account :db/id)
:invoice-expense-account/location location :invoice-expense-account/location location
:invoice-expense-account/amount (Math/abs total) :invoice-expense-account/amount (Math/abs total)
:db/id (random-tempid) :db/id (random-tempid)}]}]
}]}]
(log/info ::cintas-invoice-importing (log/info ::cintas-invoice-importing
:invoice invoice) :invoice invoice)
[invoice]) [invoice])
@@ -232,7 +227,7 @@
(s3/copy-object {:source-bucket-name bucket-name (s3/copy-object {:source-bucket-name bucket-name
:destination-bucket-name bucket-name :destination-bucket-name bucket-name
:source-key k :source-key k
:destination-key invoice-key }) :destination-key invoice-key})
invoice-key)) invoice-key))
(defn get-all-keys (defn get-all-keys
@@ -240,7 +235,7 @@
(let [first-page-result (s3/list-objects-v2 {:bucket-name bucket-name (let [first-page-result (s3/list-objects-v2 {:bucket-name bucket-name
:prefix "ntg-invoices/pending"})] :prefix "ntg-invoices/pending"})]
(lazy-seq (concat (:object-summaries first-page-result) (get-all-keys (:next-continuation-token first-page-result)))))) (lazy-seq (concat (:object-summaries first-page-result) (get-all-keys (:next-continuation-token first-page-result))))))
([next-token ] ([next-token]
(when next-token (when next-token
(let [page-result (s3/list-objects-v2 {:bucket-name bucket-name (let [page-result (s3/list-objects-v2 {:bucket-name bucket-name
:prefix "ntg-invoices/pending" :prefix "ntg-invoices/pending"
@@ -250,8 +245,7 @@
(lazy-seq (concat (:object-summaries page-result) (get-all-keys (:next-continuation-token page-result))))))))) (lazy-seq (concat (:object-summaries page-result) (get-all-keys (:next-continuation-token page-result)))))))))
(defn recent? [k] (defn recent? [k]
(time/after? (:last-modified k) (time/plus (time/now) (time/days -15))) (time/after? (:last-modified k) (time/plus (time/now) (time/days -15))))
)
(defn import-ntg-invoices (defn import-ntg-invoices
([] (import-ntg-invoices (->> (get-all-keys) ([] (import-ntg-invoices (->> (get-all-keys)
@@ -268,7 +262,7 @@
:where [?c :client/code]] :where [?c :client/code]]
(d/db conn)))] (d/db conn)))]
(log/info ::found-invoice-keys (log/info ::found-invoice-keys
:keys keys ) :keys keys)
(let [transaction (->> keys (let [transaction (->> keys
(mapcat (fn [k] (mapcat (fn [k]
(try (try
@@ -304,6 +298,5 @@
(doseq [k keys] (doseq [k keys]
(mark-key k))))) (mark-key k)))))
(defn -main [& _] (defn -main [& _]
(execute "ntg" import-ntg-invoices)) (execute "ntg" import-ntg-invoices))

View File

@@ -45,8 +45,7 @@
(reduce + 0.0 (reduce + 0.0
(->> values (->> values
(map (fn [[_ _ _ _ amount]] (map (fn [[_ _ _ _ amount]]
(- (Double/parseDouble amount)))))) (- (Double/parseDouble amount))))))]))
]))
(into {}))] (into {}))]
(->> (->>
(for [[i (for [[i
@@ -76,27 +75,22 @@
target-date (coerce/to-date (atime/parse target-date atime/normal-date)) target-date (coerce/to-date (atime/parse target-date atime/normal-date))
current-date (:invoice/date invoice) current-date (:invoice/date invoice)
current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0) current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0)
target-expense-account-amount (- (Double/parseDouble amount)) target-expense-account-amount (- (Double/parseDouble amount))
current-expense-account-location (:invoice-expense-account/location invoice-expense-account) current-expense-account-location (:invoice-expense-account/location invoice-expense-account)
target-expense-account-location location target-expense-account-location location
[[_ _ invoice-payment]] (vec (dc/q [[_ _ invoice-payment]] (vec (dc/q
'[:find ?p ?a ?ip '[:find ?p ?a ?ip
:in $ ?i :in $ ?i
:where [?ip :invoice-payment/invoice ?i] :where [?ip :invoice-payment/invoice ?i]
[?ip :invoice-payment/amount ?a] [?ip :invoice-payment/amount ?a]
[?ip :invoice-payment/payment ?p] [?ip :invoice-payment/payment ?p]]
]
db invoice-id))] db invoice-id))]
:when current-total] :when current-total]
[ [(when (not (dollars= current-total target-total))
(when (not (dollars= current-total target-total))
{:db/id invoice-id {:db/id invoice-id
:invoice/total target-total}) :invoice/total target-total})
@@ -129,7 +123,7 @@
{:db/id invoice-expense-account-id {:db/id invoice-expense-account-id
:invoice-expense-account/location target-expense-account-location}) :invoice-expense-account/location target-expense-account-location})
(when (not= current-account-id target-account-id ) (when (not= current-account-id target-account-id)
{:db/id invoice-expense-account-id {:db/id invoice-expense-account-id
:invoice-expense-account/account target-account-id})]) :invoice-expense-account/account target-account-id})])
(mapcat identity) (mapcat identity)

View File

@@ -38,9 +38,6 @@
(recur next-deps (into order next-order)) (recur next-deps (into order next-order))
(into order next-order))))) (into order next-order)))))
(def loaded (atom #{})) (def loaded (atom #{}))
(defn upsert-batch (defn upsert-batch
@@ -112,7 +109,6 @@
{:entity/migration-key 17592263907739} {:entity/migration-key 17592263907739}
{:entity/migration-key 17592271516922}]) {:entity/migration-key 17592271516922}])
(doseq [entity (cond->> (order-of-insert entity-dependencies) (doseq [entity (cond->> (order-of-insert entity-dependencies)
true (filter #(not= "audit" %)) true (filter #(not= "audit" %))
starting-at (drop-while #(not= starting-at %))) starting-at (drop-while #(not= starting-at %)))
@@ -136,7 +132,6 @@
(mu/log ::refresh-running-balance-cache-complete) (mu/log ::refresh-running-balance-cache-complete)
(mu/log ::done)) (mu/log ::done))
(defn -main [& _] (defn -main [& _]
(try (try
(println "restore") (println "restore")
@@ -156,8 +151,6 @@
;; /datomic-backup/079df203-eae0-4acf-94d5-8608ba8b8a9a ;; /datomic-backup/079df203-eae0-4acf-94d5-8608ba8b8a9a
(load-from-backup "079df203-eae0-4acf-94d5-8608ba8b8a9a" auto-ap.datomic/conn ["charge"]) (load-from-backup "079df203-eae0-4acf-94d5-8608ba8b8a9a" auto-ap.datomic/conn ["charge"])
(load-entity "charge" (ednl/slurp "/tmp/tmp-edn")) (load-entity "charge" (ednl/slurp "/tmp/tmp-edn")))
)
;; => nil ;; => nil

View File

@@ -39,18 +39,15 @@
(dc/db conn) (dc/db conn)
number))) number)))
(defn delete-all [] (defn delete-all []
@(dc/transact-async conn @(dc/transact-async conn
(->> (->>
(dc/q '[:find ?ss (dc/q '[:find ?ss
:where [?ss :sales-summary/date]] :where [?ss :sales-summary/date]]
(dc/db conn)) (dc/db conn))
(map (fn [[ ss]] (map (fn [[ss]]
[:db/retractEntity ss]))))) [:db/retractEntity ss])))))
(defn dirty-sales-summaries [c] (defn dirty-sales-summaries [c]
(let [client-id (dc/entid (dc/db conn) c)] (let [client-id (dc/entid (dc/db conn) c)]
(->> (dc/index-pull (dc/db conn) (->> (dc/index-pull (dc/db conn)
@@ -192,8 +189,6 @@
:ledger-mapped/amount v :ledger-mapped/amount v
:ledger-mapped/ledger-side :ledger-side/credit})))) :ledger-mapped/ledger-side :ledger-side/credit}))))
(defn get-fees [c date] (defn get-fees [c date]
(when-let [fee (get-fee c date)] (when-let [fee (get-fee c date)]
{:db/id (str (java.util.UUID/randomUUID)) {:db/id (str (java.util.UUID/randomUUID))
@@ -315,7 +310,6 @@
@(dc/transact conn [[:upsert-sales-summary result]])) @(dc/transact conn [[:upsert-sales-summary result]]))
@(dc/transact conn [{:db/id id :sales-summary/dirty false}])))))) @(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
(defn reset-summaries [] (defn reset-summaries []
@(dc/transact conn (->> (dc/q '[:find ?sos @(dc/transact conn (->> (dc/q '[:find ?sos
:in $ :in $
@@ -324,9 +318,6 @@
(map (fn [[sos]] (map (fn [[sos]]
[:db/retractEntity sos]))))) [:db/retractEntity sos])))))
(comment (comment
(auto-ap.datomic/transact-schema conn) (auto-ap.datomic/transact-schema conn)
@@ -336,26 +327,19 @@
(dirty-sales-summaries [:client/code "NGWH"]) (dirty-sales-summaries [:client/code "NGWH"])
(apply mark-dirty [:client/code "NGWH"] (last-n-days 5)) (apply mark-dirty [:client/code "NGWH"] (last-n-days 5))
(iol-ion.tx.upsert-sales-summary-ledger/summary->journal-entry (dc/db conn) 17592314245819) (iol-ion.tx.upsert-sales-summary-ledger/summary->journal-entry (dc/db conn) 17592314245819)
(iol-ion.tx.upsert-sales-summary-ledger/upsert-sales-summary (dc/db conn) {:db/id 17592314241429}) (iol-ion.tx.upsert-sales-summary-ledger/upsert-sales-summary (dc/db conn) {:db/id 17592314241429})
(mark-all-dirty 5) (mark-all-dirty 5)
(delete-all) (delete-all)
(sales-summaries-v2) (sales-summaries-v2)
1 1
(dc/q '[:find (pull ?sos [* {:sales-summary/sales-items [*]}]) (dc/q '[:find (pull ?sos [* {:sales-summary/sales-items [*]}])
:in $ :in $
:where [?sos :sales-summary/client [:client/code "NGHW"]] :where [?sos :sales-summary/client [:client/code "NGHW"]]
@@ -386,15 +370,7 @@
@(dc/transact conn [{:db/id :sales-summary/total-tax :db/ident :sales-summary/total-tax-legacy} @(dc/transact conn [{:db/id :sales-summary/total-tax :db/ident :sales-summary/total-tax-legacy}
{:db/id :sales-summary/total-tip :db/ident :sales-summary/total-tip-legacy}]) {:db/id :sales-summary/total-tip :db/ident :sales-summary/total-tip-legacy}])
(auto-ap.datomic/transact-schema conn) (auto-ap.datomic/transact-schema conn))
)
(defn -main [& _] (defn -main [& _]
(execute "sales-summaries" sales-summaries-v2)) (execute "sales-summaries" sales-summaries-v2))

View File

@@ -44,7 +44,6 @@
(dc/db conn) (dc/db conn)
50000)))) 50000))))
(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"])
@@ -64,7 +63,6 @@
first first
first))) first)))
(defn read-sysco-csv [k] (defn read-sysco-csv [k]
(-> (s3/get-object {:bucket-name bucket-name (-> (s3/get-object {:bucket-name bucket-name
:key k}) :key k})
@@ -84,11 +82,9 @@
(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))
(fnil + 0.0) (fnil + 0.0)
(Double/parseDouble (nth row item-price-index)) (Double/parseDouble (nth row item-price-index))))
)
) {}))
{})
)
items-with-tax (update items (get-line-account "TAX") items-with-tax (update items (get-line-account "TAX")
(fnil + 0.0) (fnil + 0.0)
tax) tax)
@@ -153,9 +149,9 @@
:client/locations] :client/locations]
(:db/id matching-client)) (:db/id matching-client))
location-hint location-hint
location-hint ) location-hint)
:date (coerce/to-date date) :date (coerce/to-date date)
:vendor (:db/id sysco-vendor ) :vendor (:db/id sysco-vendor)
:client (:db/id matching-client) :client (:db/id matching-client)
:import-status :import-status/imported :import-status :import-status/imported
:status :invoice-status/unpaid :status :invoice-status/unpaid
@@ -182,39 +178,30 @@
(defn get-test-invoice-file (defn get-test-invoice-file
([] (get-test-invoice-file 999)) ([] (get-test-invoice-file 999))
( [i] ([i]
(nth (->> (s3/list-objects-v2 {:bucket-name "data.prod.app.integreatconsult.com" (nth (->> (s3/list-objects-v2 {:bucket-name "data.prod.app.integreatconsult.com"
:prefix "sysco/imported"}) :prefix "sysco/imported"})
:object-summaries :object-summaries
(map :key) (map :key))
)
i))) i)))
(comment (comment
(with-bindings { #'bucket-name "data.prod.app.integreatconsult.com"} (with-bindings {#'bucket-name "data.prod.app.integreatconsult.com"}
(doall (doall
(for [n (range 930 940 ) (for [n (range 930 940)
:let [result (-> (get-test-invoice-file n) :let [result (-> (get-test-invoice-file n)
read-sysco-csv read-sysco-csv
(extract-invoice-details (get-sysco-vendor)) (extract-invoice-details (get-sysco-vendor)))]
)]
#_#_:when (not (check-okay-amount? result))] #_#_:when (not (check-okay-amount? result))]
result))) result)))
(with-bindings { #'bucket-name "data.prod.app.integreatconsult.com"} (with-bindings {#'bucket-name "data.prod.app.integreatconsult.com"}
(let [result (-> "sysco/error/SYSCO050_00175962_20241010122639019.csv" (let [result (-> "sysco/error/SYSCO050_00175962_20241010122639019.csv"
read-sysco-csv read-sysco-csv
(extract-invoice-details (get-sysco-vendor)) (extract-invoice-details (get-sysco-vendor)))]
)]
result)) result)))
)
(defn import-sysco [] (defn import-sysco []
(let [sysco-vendor (get-sysco-vendor) (let [sysco-vendor (get-sysco-vendor)
@@ -223,7 +210,6 @@
:object-summaries :object-summaries
(map :key))] (map :key))]
(alog/info ::importing-sysco (alog/info ::importing-sysco
:count (count keys) :count (count keys)
:keys (pr-str keys)) :keys (pr-str keys))
@@ -256,6 +242,5 @@
(doseq [k keys] (doseq [k keys]
(mark-key k)))) (mark-key k))))
(defn -main [& _] (defn -main [& _]
(execute "sysco" import-sysco)) (execute "sysco" import-sysco))

View File

@@ -18,23 +18,20 @@
(t/plus (t/months -6)) (t/plus (t/months -6))
(c/to-date)))) (c/to-date))))
([start-date] ([start-date]
(let [txes-missing-ledger-entries (->> (dc/q {:find ['?t ] (let [txes-missing-ledger-entries (->> (dc/q {:find ['?t]
:in ['$ '?sd] :in ['$ '?sd]
:where [ :where ['[?t :transaction/date ?d]
'[?t :transaction/date ?d]
'[(>= ?d ?sd)] '[(>= ?d ?sd)]
'(not [_ :journal-entry/original-entity ?t]) '(not [_ :journal-entry/original-entity ?t])
'(not [?t :transaction/amount 0.0]) '(not [?t :transaction/amount 0.0])
'(not [?t :transaction/approval-status :transaction-approval-status/excluded]) '(not [?t :transaction/approval-status :transaction-approval-status/excluded])
'(not [?t :transaction/approval-status :transaction-approval-status/suppressed]) '(not [?t :transaction/approval-status :transaction-approval-status/suppressed])]}
]}
(dc/db conn) start-date) (dc/db conn) start-date)
(map first) (map first)
(mapv (fn [t] (mapv (fn [t]
[:upsert-transaction {:db/id t}]))) [:upsert-transaction {:db/id t}])))
invoices-missing-ledger-entries (->> (dc/q {:find ['?t]
invoices-missing-ledger-entries (->> (dc/q {:find ['?t ]
:in ['$ '?sd] :in ['$ '?sd]
:where ['[?t :invoice/date ?d] :where ['[?t :invoice/date ?d]
'[(>= ?d ?sd)] '[(>= ?d ?sd)]
@@ -43,21 +40,19 @@
'[(not= 0.0 ?amt)] '[(not= 0.0 ?amt)]
'(not [?t :invoice/status :invoice-status/voided]) '(not [?t :invoice/status :invoice-status/voided])
'(not [?t :invoice/import-status :import-status/pending]) '(not [?t :invoice/import-status :import-status/pending])
'(not [?t :invoice/exclude-from-ledger true]) '(not [?t :invoice/exclude-from-ledger true])]}
]}
(dc/db conn) start-date) (dc/db conn) start-date)
(map first) (map first)
(mapv (fn [i] (mapv (fn [i]
[:upsert-invoice {:db/id i}]))) [:upsert-invoice {:db/id i}])))
sales-summaries-missing-ledger-entries (->> (dc/q {:find ['?ss ] sales-summaries-missing-ledger-entries (->> (dc/q {:find ['?ss]
:in ['$ '?sd] :in ['$ '?sd]
:where ['[?ss :sales-summary/date ?d] :where ['[?ss :sales-summary/date ?d]
'[(>= ?d ?sd)] '[(>= ?d ?sd)]
'(not [_ :journal-entry/original-entity ?ss]) '(not [_ :journal-entry/original-entity ?ss])
'[?ss :sales-summary/items ?item] '[?ss :sales-summary/items ?item]
'[?item :ledger-mapped/account] '[?item :ledger-mapped/account]]}
]}
(dc/db conn) start-date) (dc/db conn) start-date)
(map first) (map first)
(mapv (fn [ss] (mapv (fn [ss]
@@ -72,7 +67,6 @@
:sales-summary-count (count sales-summaries-missing-ledger-entries)) :sales-summary-count (count sales-summaries-missing-ledger-entries))
@(dc/transact conn repairs))))) @(dc/transact conn repairs)))))
(defn touch-transaction [e] (defn touch-transaction [e]
@(dc/transact conn [{:db/id "datomic.tx" @(dc/transact conn [{:db/id "datomic.tx"
:db/doc "touching transaction to update ledger"} :db/doc "touching transaction to update ledger"}
@@ -83,8 +77,6 @@
:db/doc "touching invoice to update ledger"} :db/doc "touching invoice to update ledger"}
[:upsert-invoice {:db/id e}]])) [:upsert-invoice {:db/id e}]]))
(defn recently-changed-entities [start end] (defn recently-changed-entities [start end]
(into #{} (into #{}
(map first) (map first)
@@ -97,7 +89,7 @@
(defn mismatched-transactions (defn mismatched-transactions
([] ([]
(mismatched-transactions (c/to-date (t/minus (t/now) (t/days 7))) (mismatched-transactions (c/to-date (t/minus (t/now) (t/days 7)))
(c/to-date (t/minus (t/now) (t/hours 1)))) ) (c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end] ([changed-between-start changed-between-end]
(mu/trace ::calculating-mismatched-transactions (mu/trace ::calculating-mismatched-transactions
[:range {:start changed-between-start [:range {:start changed-between-start
@@ -109,7 +101,7 @@
:count (count entities-to-consider)) :count (count entities-to-consider))
jel-accounts (reduce jel-accounts (reduce
(fn [acc [e lia]] (fn [acc [e lia]]
(update acc e (fnil conj #{} ) lia)) (update acc e (fnil conj #{}) lia))
{} {}
(dc/q '[:find ?e ?lia (dc/q '[:find ?e ?lia
:in $ [?e ...] :in $ [?e ...]
@@ -123,7 +115,7 @@
entities-to-consider)) entities-to-consider))
transaction-accounts (reduce transaction-accounts (reduce
(fn [acc [e lia]] (fn [acc [e lia]]
(update acc e (fnil conj #{} ) lia)) (update acc e (fnil conj #{}) lia))
{} {}
(dc/q '[:find ?e ?lia (dc/q '[:find ?e ?lia
:in $ [?e ...] :in $ [?e ...]
@@ -154,8 +146,7 @@
:where [?je :journal-entry/amount ?a] :where [?je :journal-entry/amount ?a]
[?je :journal-entry/line-items ?jel] [?je :journal-entry/line-items ?jel]
[(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit] [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
[(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit] [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]]
]
(dc/db conn) (dc/db conn)
entities-to-consider) entities-to-consider)
(filter (fn [[_ a d c]] (filter (fn [[_ a d c]]
@@ -165,8 +156,6 @@
(map (fn [je] (map (fn [je]
(pull-ref (dc/db conn) :journal-entry/original-entity je))))))) (pull-ref (dc/db conn) :journal-entry/original-entity je)))))))
(defn unbalanced-invoices (defn unbalanced-invoices
([] (unbalanced-invoices (c/to-date (t/minus (t/now) (t/days 7))) ([] (unbalanced-invoices (c/to-date (t/minus (t/now) (t/days 7)))
(c/to-date (t/minus (t/now) (t/hours 1))))) (c/to-date (t/minus (t/now) (t/hours 1)))))
@@ -195,13 +184,13 @@
(defn mismatched-invoices (defn mismatched-invoices
([] ([]
(mismatched-invoices (c/to-date (t/minus (t/now) (t/days 7))) (mismatched-invoices (c/to-date (t/minus (t/now) (t/days 7)))
(c/to-date (t/minus (t/now) (t/hours 1)))) ) (c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end] ([changed-between-start changed-between-end]
(let [entities-to-consider (recently-changed-entities changed-between-start changed-between-end) (let [entities-to-consider (recently-changed-entities changed-between-start changed-between-end)
jel-accounts (reduce jel-accounts (reduce
(fn [acc [e lia]] (fn [acc [e lia]]
(update acc e (fnil conj #{} ) lia)) (update acc e (fnil conj #{}) lia))
{} {}
(dc/q '[:find ?e ?lia (dc/q '[:find ?e ?lia
:in $ [?e ...] :in $ [?e ...]
@@ -216,7 +205,7 @@
entities-to-consider)) entities-to-consider))
invoice-accounts (reduce invoice-accounts (reduce
(fn [acc [e lia]] (fn [acc [e lia]]
(update acc e (fnil conj #{} ) lia)) (update acc e (fnil conj #{}) lia))
{} {}
(dc/q '[:find ?e ?lia (dc/q '[:find ?e ?lia
:in $ [?e ...] :in $ [?e ...]
@@ -230,8 +219,7 @@
(not [?e :invoice/exclude-from-ledger true]) (not [?e :invoice/exclude-from-ledger true])
[?e :invoice/import-status :import-status/imported]] [?e :invoice/import-status :import-status/imported]]
(dc/db conn) (dc/db conn)
entities-to-consider)) entities-to-consider))]
]
(filter (filter
(fn [[e accounts]] (fn [[e accounts]]
(not= accounts (get jel-accounts e))) (not= accounts (get jel-accounts e)))
@@ -272,7 +260,6 @@
(statsd/gauge "data.unbalanced_transactions" (count (unbalanced-transactions)))) (statsd/gauge "data.unbalanced_transactions" (count (unbalanced-transactions))))
(statsd/gauge "data.unbalanced_transactions" 0.0)))) (statsd/gauge "data.unbalanced_transactions" 0.0))))
(mu/trace ::fixing-mismatched-invoices (mu/trace ::fixing-mismatched-invoices
[] []
(mu/log ::started-fixing-mismatched-invoices) (mu/log ::started-fixing-mismatched-invoices)
@@ -308,16 +295,14 @@
:priority :low} :priority :low}
nil)) nil))
(defn build-account-lookup [client-id] (defn build-account-lookup [client-id]
(let [accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :account/name (let [accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :account/name
:account/numeric-code :account/numeric-code
{:account/type [:db/ident] {:account/type [:db/ident]
:account/client-overrides [:account-client-override/client :account-client-override/name]} :account/client-overrides [:account-client-override/client :account-client-override/name]}])]
])]
:in ['$] :in ['$]
:where ['[?e :account/name]]} :where ['[?e :account/name]]}
(dc/db conn )))) (dc/db conn))))
bank-accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :bank-account/name :bank-account/numeric-code {:bank-account/type [:db/ident]}])] bank-accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :bank-account/name :bank-account/numeric-code {:bank-account/type [:db/ident]}])]
:in ['$] :in ['$]
@@ -329,9 +314,8 @@
(map (fn [o] (map (fn [o]
[[(: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 {} ))]
(fn [a] (fn [a]
{:name (or (:bank-account/name (bank-accounts a)) {:name (or (:bank-account/name (bank-accounts a))
(overrides-by-client [a client-id]) (overrides-by-client [a client-id])
@@ -356,8 +340,7 @@
:selector [:db/id :journal-entry-line/location :journal-entry-line/account :journal-entry-line/client+account+location+date {:journal-entry/_line-items [:journal-entry/date :journal-entry/client]}] :selector [:db/id :journal-entry-line/location :journal-entry-line/account :journal-entry-line/client+account+location+date {:journal-entry/_line-items [:journal-entry/date :journal-entry/client]}]
:start [:journal-entry-line/client+account+location+date [c]]}) :start [:journal-entry-line/client+account+location+date [c]]})
(take-while (fn [result] (take-while (fn [result]
(= c (first (:journal-entry-line/client+account+location+date result))) (= c (first (:journal-entry-line/client+account+location+date result)))))
))
(filter (fn [{index :journal-entry-line/client+account+location+date :as result}] (filter (fn [{index :journal-entry-line/client+account+location+date :as result}]
(not= index (not= index
[(-> result :journal-entry/_line-items :journal-entry/client :db/id) [(-> result :journal-entry/_line-items :journal-entry/client :db/id)
@@ -383,7 +366,7 @@
(filter (comp available :db/id) clients) (filter (comp available :db/id) clients)
clients))) clients)))
#_(clients-needing-refresh (dc/db conn) #{ 17592273679867}) #_(clients-needing-refresh (dc/db conn) #{17592273679867})
#_(comment [17592334354011 #inst "0024-08-03T07:52:58.000-00:00"] #_(comment [17592334354011 #inst "0024-08-03T07:52:58.000-00:00"]
[17592302554688 #inst "0023-07-20T07:52:58.000-00:00"] [17592302554688 #inst "0023-07-20T07:52:58.000-00:00"]
@@ -502,10 +485,10 @@
#_(do #_(do
(mu/with-context {:service "upsert-running-balance" (mu/with-context {:service "upsert-running-balance"
:source "upsert-running-balance" } :source "upsert-running-balance"}
(mu/trace ::updating-balance (mu/trace ::updating-balance
[:service "upsert-running-balance" [:service "upsert-running-balance"
:source "upsert-running-balance" ] :source "upsert-running-balance"]
(let [db (dc/db conn) (let [db (dc/db conn)
starting-at (c/to-date (t/now)) starting-at (c/to-date (t/now))
_ (mark-client-dirty "NGA1") _ (mark-client-dirty "NGA1")
@@ -556,9 +539,9 @@
(mark-client-dirty "NGA1") (mark-client-dirty "NGA1")
(mark-all-clients-dirty) (mark-all-clients-dirty)
(count (clients-needing-refresh (dc/db conn))) (count (clients-needing-refresh (dc/db conn)))
(upsert-running-balance) (upsert-running-balance)
@@ -580,18 +563,14 @@
:journal-entry-line/date date :journal-entry-line/date date
:journal-entry-line/client client}) :journal-entry-line/client client})
{:user/name "backfill-client and dates"}) {:user/name "backfill-client and dates"})
(println "done.")) (println "done."))
#_(dc/q '[:find (pull ?je [*]) (pull ?jel [*]) #_(dc/q '[:find (pull ?je [*]) (pull ?jel [*])
:where [?je :journal-entry/line-items ?jel] :where [?je :journal-entry/line-items ?jel]
(not [?jel :journal-entry-line/running-balance-tuple])] (not [?jel :journal-entry-line/running-balance-tuple])]
(dc/db conn))) (dc/db conn)))
;; TODO only enable once IOL is set up in clod ;; TODO only enable once IOL is set up in clod
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(mount/defstate running-balance-cache-worker (mount/defstate running-balance-cache-worker

View File

@@ -1,7 +1,6 @@
(ns auto-ap.logging (ns auto-ap.logging
(:require [com.brunobonacci.mulog :as mu])) (:require [com.brunobonacci.mulog :as mu]))
(defmacro with-context-as [ctx s & body] (defmacro with-context-as [ctx s & body]
`(mu/with-context ~ctx `(mu/with-context ~ctx
(let [~s (mu/local-context)] (let [~s (mu/local-context)]
@@ -12,13 +11,13 @@
~@body)) ~@body))
(defmacro info [x & kvs] (defmacro info [x & kvs]
`(mu/log ~x :status "INFO" ~@kvs )) `(mu/log ~x :status "INFO" ~@kvs))
(defmacro warn [x & kvs] (defmacro warn [x & kvs]
`(mu/log ~x :status "WARN" ~@kvs )) `(mu/log ~x :status "WARN" ~@kvs))
(defmacro error [x & kvs] (defmacro error [x & kvs]
`(mu/log ~x :status "ERROR" ~@kvs )) `(mu/log ~x :status "ERROR" ~@kvs))
(defn peek (defn peek
([x] ([x]

View File

@@ -16,7 +16,6 @@
(defonce last-text (atom nil)) (defonce last-text (atom nil))
(defn template-applies? [text {:keys [keywords]}] (defn template-applies? [text {:keys [keywords]}]
(every? #(re-find % text) keywords)) (every? #(re-find % text) keywords))
@@ -60,8 +59,6 @@
first first
(extract-template text))) (extract-template text)))
(defmulti parse-file (defmulti parse-file
"Parses a file based on its extension. Accepts options as additional arguments. "Parses a file based on its extension. Accepts options as additional arguments.
Options: Options:
@@ -75,7 +72,6 @@
(json/write-str (json/write-str
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f)}))})))] (alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f)}))})))]
(alog/info ::glimpse2-payload :payload result) (alog/info ::glimpse2-payload :payload result)
(-> result (-> result
json/read-str))) json/read-str)))
@@ -123,7 +119,6 @@
[file filename & _] [file filename & _]
(excel/parse-file file filename)) (excel/parse-file file filename))
(defmethod parse-file (defmethod parse-file
"xlsx" "xlsx"
[file filename & _] [file filename & _]

View File

@@ -23,7 +23,6 @@
(str/includes? (str header) "Document Number") (str/includes? (str header) "Document Number")
:philz :philz
(str/includes? (str header) "DISCOUNT_MESSAGE") (str/includes? (str header) "DISCOUNT_MESSAGE")
:wismettac :wismettac
@@ -53,8 +52,7 @@
(try (try
(u/parse-value :clj-time f d) (u/parse-value :clj-time f d)
(catch Exception _ (catch Exception _
nil)) nil))))
))
(first))] (first))]
(u/parse-value :clj-time valid-fmt d))) (u/parse-value :clj-time valid-fmt d)))
@@ -109,7 +107,7 @@
(map (fn [[_ po-number _ invoice-number invoice-date customer value :as row]] (map (fn [[_ po-number _ invoice-number invoice-date customer value :as row]]
{:vendor-code "Mama Lu's Foods" {:vendor-code "Mama Lu's Foods"
:customer-identifier customer :customer-identifier customer
:invoice-number (str po-number "-" invoice-number ) :invoice-number (str po-number "-" invoice-number)
:date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"]) :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
:total (str/replace value #"," "") :total (str/replace value #"," "")
:text (str/join " " row) :text (str/join " " row)
@@ -122,10 +120,10 @@
[rows] [rows]
(transduce (transduce
(comp (drop 1) (comp (drop 1)
(map (fn [[ po-number _ invoice-number invoice-date customer value :as row]] (map (fn [[po-number _ invoice-number invoice-date customer value :as row]]
{:vendor-code "Mama Lu's Foods" {:vendor-code "Mama Lu's Foods"
:customer-identifier customer :customer-identifier customer
:invoice-number (str po-number "-" invoice-number ) :invoice-number (str po-number "-" invoice-number)
:date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"]) :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
:total (str/replace value #"," "") :total (str/replace value #"," "")
:text (str/join " " row) :text (str/join " " row)
@@ -138,7 +136,7 @@
[rows] [rows]
(transduce (transduce
(comp (comp
(filter (fn [[_ _ _ _ _ status _ _ _ ]] (filter (fn [[_ _ _ _ _ status _ _ _]]
(= status "Billed"))) (= status "Billed")))
(map (fn [[dt _ doc-number name _ _ _ _ amount :as row]] (map (fn [[dt _ doc-number name _ _ _ _ amount :as row]]
{:vendor-code "PHILZ COFFEE, INC" {:vendor-code "PHILZ COFFEE, INC"
@@ -147,10 +145,8 @@
:date (some-> dt not-empty (parse-date-fallover ["MM/dd/yyyy"])) :date (some-> dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
:total (str/replace amount #"," "") :total (str/replace amount #"," "")
:text (str/join " " row) :text (str/join " " row)
:full-text (str/join " " row)})) :full-text (str/join " " row)})))
)
conj conj
[] []
(drop 1 rows))) (drop 1 rows)))
@@ -165,8 +161,7 @@
:date (some-> inv_dt not-empty (parse-date-fallover ["MM/dd/yyyy"])) :date (some-> inv_dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
:total (str/replace total #"," "") :total (str/replace total #"," "")
:text (str/join " " row) :text (str/join " " row)
:full-text (str/join " " row)})) :full-text (str/join " " row)})))
)
conj conj
[] []
(drop 1 rows))) (drop 1 rows)))
@@ -182,8 +177,7 @@
:due (some-> due not-empty (parse-date-fallover ["MM/dd/yy"])) :due (some-> due not-empty (parse-date-fallover ["MM/dd/yy"]))
:total (str/replace amount #"[\$,]" "") :total (str/replace amount #"[\$,]" "")
:text (str/join " " row) :text (str/join " " row)
:full-text (str/join " " row)})) :full-text (str/join " " row)})))
)
conj conj
[] []
(drop 1 rows))) (drop 1 rows)))
@@ -199,8 +193,7 @@
:date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"])) :date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
:total (str/replace amount #"[\$,]" "") :total (str/replace amount #"[\$,]" "")
:text (str/join " " row) :text (str/join " " row)
:full-text (str/join " " row)})) :full-text (str/join " " row)})))
)
conj conj
[] []
(drop 1 rows))) (drop 1 rows)))

View File

@@ -6,10 +6,7 @@
[clojure.data.json :as json] [clojure.data.json :as json]
[config.core :refer [env]] [config.core :refer [env]]
[clojure.java.io :as io] [clojure.java.io :as io]
[amazonica.aws.s3 :as s3]) [amazonica.aws.s3 :as s3]))
)
(defn template-applies? [text {:keys [keywords]}] (defn template-applies? [text {:keys [keywords]}]
@@ -29,16 +26,15 @@
(re-find regex (str (d/read-cell cell))))) (re-find regex (str (d/read-cell cell)))))
(map (fn [cell] (map (fn [cell]
(let [address (.getAddress cell) (let [address (.getAddress cell)
cell-value (str (d/read-cell (d/select-cell (.toString (CellAddress. (+ offset-row (.getRow address)) (+ offset-column (.getColumn address)) )) cell-value (str (d/read-cell (d/select-cell (.toString (CellAddress. (+ offset-row (.getRow address)) (+ offset-column (.getColumn address))))
(first (d/sheet-seq wb))))) (first (d/sheet-seq wb)))))
raw-result (if extract-regex raw-result (if extract-regex
(second (re-find extract-regex cell-value)) (second (re-find extract-regex cell-value))
cell-value)] cell-value)]
(if (get parser k) (if (get parser k)
(u/parse-value (first (get parser k) ) (second (get parser k) ) raw-result) (u/parse-value (first (get parser k)) (second (get parser k)) raw-result)
raw-result raw-result))))
))))
first))) first)))
{:vendor-code vendor} {:vendor-code vendor}
extract)])) extract)]))
@@ -67,9 +63,6 @@
first first
(extract sheet)))) (extract sheet))))
(defn xls-date->date [f] (defn xls-date->date [f]
(when (not-empty f) (when (not-empty f)
(let [f (Double/parseDouble f) (let [f (Double/parseDouble f)

View File

@@ -8,11 +8,9 @@
(defmulti parse-value (fn [method _ _] (defmulti parse-value (fn [method _ _]
method)) method))
(defmethod parse-value :trim-commas (defmethod parse-value :trim-commas
[_ _ value] [_ _ value]
(str/replace value #"," "") (str/replace value #"," ""))
)
(defmethod parse-value :trim-commas-and-remove-dollars (defmethod parse-value :trim-commas-and-remove-dollars
[_ _ value] [_ _ value]
(str/replace (str/replace value #"," "") #"\$" "")) (str/replace (str/replace value #"," "") #"\$" ""))
@@ -20,7 +18,7 @@
(defmethod parse-value :trim-commas-and-remove-dollars-and-invert-parentheses (defmethod parse-value :trim-commas-and-remove-dollars-and-invert-parentheses
[_ _ value] [_ _ value]
(let [v (str/replace (str/replace value #"," "") #"\$" "")] (let [v (str/replace (str/replace value #"," "") #"\$" "")]
(if-let [[_ a ] (re-find #"\((.*)\)" v)] (if-let [[_ a] (re-find #"\((.*)\)" v)]
(str "-" a) (str "-" a)
v))) v)))
@@ -40,11 +38,10 @@
(let [format "yyyy-MM-dd" (let [format "yyyy-MM-dd"
[month day year] (str/split (-> value [month day year] (str/split (-> value
(str/replace #"\s+" " ") (str/replace #"\s+" " "))
)
#"\s") #"\s")
value (str "20" year "-" month "-" day) ] value (str "20" year "-" month "-" day)]
(try (try
(time/from-time-zone (f/parse (f/formatter format) value) (time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles")) (time/time-zone-for-id "America/Los_Angeles"))
@@ -66,8 +63,7 @@
(alog/warn ::cant-parse-date :error e :raw-value (str value)) (alog/warn ::cant-parse-date :error e :raw-value (str value))
nil))) nil)))
nil nil
format) format)))
))
(defmethod parse-value nil (defmethod parse-value nil
[_ _ value] [_ _ value]

View File

@@ -47,8 +47,7 @@
(:color cell) (assoc :color (:color cell)) (:color cell) (assoc :color (:color cell))
(:bg-color cell) (assoc :background-color (:bg-color cell))) (:bg-color cell) (assoc :background-color (:bg-color cell)))
cell-contents cell-contents]))
]))
(defn cell-count [table] (defn cell-count [table]
(let [counts (map count (:rows table))] (let [counts (map count (:rows table))]
@@ -77,15 +76,13 @@
:else :else
100)} 100)}
widths widths]
]
(into (into
(for [row (:rows table)] (for [row (:rows table)]
(into [] (into []
(for [cell (take cell-count (concat row (repeat nil)))] (for [cell (take cell-count (concat row (repeat nil)))]
(cell->pdf cell) (cell->pdf cell)))))
))))
(conj (take cell-count (repeat (cell->pdf {:value " "}))))))) (conj (take cell-count (repeat (cell->pdf {:value " "})))))))
(defn split-table [table n] (defn split-table [table n]
@@ -177,7 +174,7 @@
(conj [:paragraph {:color [128 0 0] :size 9} (:warning report)]) (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
(conj (conj
(table->pdf report (table->pdf report
(cond-> (into [30 ] (repeat client-count 13)) (cond-> (into [30] (repeat client-count 13))
(:include-comparison args) (into (repeat (* 2 client-count) 13)) (:include-comparison args) (into (repeat (* 2 client-count) 13))
(and (> client-count 1) (not (:include-comparison args))) (conj 13))))) (and (> client-count 1) (not (:include-comparison args))) (conj 13)))))
output-stream) output-stream)
@@ -192,10 +189,8 @@
(mapcat (fn [p1 p2] (mapcat (fn [p1 p2]
(map (map
(fn [a] (fn [a]
(assoc a :period p1) (assoc a :period p1))
) (:accounts p2)))
(:accounts p2))
)
(:periods args))) (:periods args)))
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients)) pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
report (l-reports/summarize-pnl pnl-data) report (l-reports/summarize-pnl pnl-data)
@@ -203,11 +198,11 @@
(pdf/pdf (pdf/pdf
(-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15 (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
:size (cond :size (cond
(and (>= (count (-> pnl-data :args :periods)) 8 ) (and (>= (count (-> pnl-data :args :periods)) 8)
(-> pnl-data :args :include-deltas)) (-> pnl-data :args :include-deltas))
:a2 :a2
(>= (count (-> pnl-data :args :periods)) 4 ) (>= (count (-> pnl-data :args :periods)) 4)
:tabloid :tabloid
:else :else
:letter) :letter)
@@ -238,10 +233,8 @@
(mapcat (fn [p1 p2] (mapcat (fn [p1 p2]
(map (map
(fn [a] (fn [a]
(assoc a :period p1) (assoc a :period p1))
) (:accounts p2)))
(:accounts p2))
)
(:periods args))) (:periods args)))
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients)) pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
report (l-reports/summarize-cash-flows pnl-data) report (l-reports/summarize-cash-flows pnl-data)
@@ -249,11 +242,11 @@
(pdf/pdf (pdf/pdf
(-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15 (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
:size (cond :size (cond
(and (>= (count (-> pnl-data :args :periods)) 8 ) (and (>= (count (-> pnl-data :args :periods)) 8)
(-> pnl-data :args :include-deltas)) (-> pnl-data :args :include-deltas))
:a2 :a2
(>= (count (-> pnl-data :args :periods)) 4 ) (>= (count (-> pnl-data :args :periods)) 4)
:tabloid :tabloid
:else :else
:letter) :letter)
@@ -296,7 +289,7 @@
(.toByteArray output-stream))) (.toByteArray output-stream)))
(defn join-names [client-ids] (defn join-names [client-ids]
(str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_" )) (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_"))
(defn pnl-args->name [args] (defn pnl-args->name [args]
(let [min-date (atime/unparse-local (let [min-date (atime/unparse-local
@@ -354,7 +347,7 @@
:report/creator (:user user) :report/creator (:user user)
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url })) :report/url url}))
(defn print-cash-flows [user args data] (defn print-cash-flows [user args data]
(let [uuid (str (UUID/randomUUID)) (let [uuid (str (UUID/randomUUID))
@@ -375,7 +368,7 @@
:report/creator (:user user) :report/creator (:user user)
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url })) :report/url url}))
(defn print-balance-sheet [user args data] (defn print-balance-sheet [user args data]
(let [uuid (str (UUID/randomUUID)) (let [uuid (str (UUID/randomUUID))
@@ -396,7 +389,7 @@
:report/creator (:user user) :report/creator (:user user)
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url })) :report/url url}))
(defn print-journal-detail-report [user args data] (defn print-journal-detail-report [user args data]
(let [uuid (str (UUID/randomUUID)) (let [uuid (str (UUID/randomUUID))
@@ -417,4 +410,4 @@
:report/creator (:user user) :report/creator (:user user)
:report/created (java.util.Date.)}]) :report/created (java.util.Date.)}])
{:report/name name {:report/name name
:report/url url })) :report/url url}))

View File

@@ -40,8 +40,6 @@
:body :body
:link_token)) :link_token))
(defn exchange-public-token [public-token _] (defn exchange-public-token [public-token _]
(-> (client/post (str base-url "/item/public_token/exchange") (-> (client/post (str base-url "/item/public_token/exchange")
{:as :json {:as :json
@@ -87,9 +85,7 @@
(.getMessage (:throwable &throw-context))) (.getMessage (:throwable &throw-context)))
json)))))) json))))))
(defn get-balance [access-token]
(defn get-balance [access-token ]
(-> (client/post (str base-url "/accounts/balance/get") (-> (client/post (str base-url "/accounts/balance/get")
{:as :json {:as :json
:headers {"Content-Type" "application/json"} :headers {"Content-Type" "application/json"}
@@ -104,7 +100,6 @@
:end (str end) :end (str end)
:acct (str account-id)) :acct (str account-id))
(try+ (try+
(-> (client/post (str base-url "/transactions/get") (-> (client/post (str base-url "/transactions/get")
{:as :json {:as :json
@@ -140,6 +135,4 @@
(clojure.pprint/pprint (clojure.pprint/pprint
(get-transactions "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4" "VZ8Y1azZMdhoYo9MQABrfpgz4jm4kPtakyxN5" #clj-time/date-time "2024-03-15" #clj-time/date-time "2024-03-30")) (get-transactions "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4" "VZ8Y1azZMdhoYo9MQABrfpgz4jm4kPtakyxN5" #clj-time/date-time "2024-03-15" #clj-time/date-time "2024-03-30"))
(clojure.pprint/pprint (get-accounts "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4")) (clojure.pprint/pprint (get-accounts "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4")))
)

View File

@@ -76,10 +76,8 @@
(double? v) (double? v)
(str v) (str v)
:else :else
v) v)]))
]))
m)) m))
(defn export-invoices [{:keys [query-params identity]}] (defn export-invoices [{:keys [query-params identity]}]
@@ -90,7 +88,7 @@
(list (into (list) (list (into (list)
(map datomic-map->graphql-map) (map datomic-map->graphql-map)
(map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id (map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
{ :invoice/status [:db/ident] {:invoice/status [:db/ident]
:invoice/payments :invoice/payments
[:invoice-payment/amount [:invoice-payment/amount
{:invoice-payment/payment [:payment/check-number {:invoice-payment/payment [:payment/check-number
@@ -124,13 +122,11 @@
[:invoices [[:invoice [:id :original-id]] :amount]] [:invoices [[:invoice [:id :original-id]] :amount]]
[:bank-account [:number :code :bank-name :bank-code :id]] [:bank-account [:number :code :bank-name :bank-code :id]]
[:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]] [:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
[:client [:id :name :code]] [:client [:id :name :code]]]]]
]]] payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [[:client/code (query-params "client-code")]]})]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [ [:client/code (query-params "client-code")]]})]
{:body {:body
(list (:all-payments (:data payments)))}))) (list (:all-payments (:data payments)))})))
(defn export-sales [{:keys [query-params identity]}] (defn export-sales [{:keys [query-params identity]}]
(assert-admin identity) (assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params) (statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
@@ -153,7 +149,7 @@
[:line_items [:item_name :total :tax :discount :category]] [:line_items [:item_name :total :tax :discount :category]]
[:client [:id :name :code]]]]] [:client [:id :name :code]]]]]
payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)})) payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
parsedouble #(some-> % Double/parseDouble) ] parsedouble #(some-> % Double/parseDouble)]
{:body {:body
(seq (map (seq (map
(fn [s] (fn [s]
@@ -206,13 +202,11 @@
(update :total #(some-> % Double/parseDouble)))) (update :total #(some-> % Double/parseDouble))))
(:all-expected-deposits (:data payments))))}))) (:all-expected-deposits (:data payments))))})))
(generate/add-encoder org.joda.time.DateTime (generate/add-encoder org.joda.time.DateTime
(fn [c jsonGenerator] (fn [c jsonGenerator]
(.writeString jsonGenerator (str c)))) (.writeString jsonGenerator (str c))))
(defn export-clients [{:keys [identity]}]
(defn export-clients[{:keys [identity]}]
(assert-admin identity) (assert-admin identity)
{:body (into [] {:body (into []
(map <-graphql) (map <-graphql)
@@ -262,11 +256,10 @@
(-> v :vendor/address :address/city) (-> v :vendor/address :address/city)
(-> v :vendor/address :address/state) (-> v :vendor/address :address/state)
(-> v :vendor/address :address/zip) (-> v :vendor/address :address/zip)
(-> v (vendor/terms-for-client-id client) ) (-> v (vendor/terms-for-client-id client))
(-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name) (-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
(-> v (vendor/account-for-client-id client) :account/numeric-code) (-> v (vendor/account-for-client-id client) :account/numeric-code)]))
]
))
(into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))] (into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
{:body {:body
(into [] (into []
@@ -386,7 +379,7 @@
{:id (:db/id e) {:id (:db/id e)
:date (:transaction/date e) :date (:transaction/date e)
:post_date (:transaction/post-date e) :post_date (:transaction/post-date e)
:client { :code (:client/code client) :client {:code (:client/code client)
:id (:db/id client) :id (:db/id client)
:name (:client/name client)} :name (:client/name client)}
:amount (:transaction/amount e) :amount (:transaction/amount e)
@@ -405,7 +398,6 @@
#_#_:original-id (Integer/parseInt (query-params "original")) #_#_:original-id (Integer/parseInt (query-params "original"))
:count Integer/MAX_VALUE})] :count Integer/MAX_VALUE})]
{:body (map {:body (map
(comp ->graphql (comp ->graphql
(fn [i] (fn [i]
@@ -459,10 +451,10 @@
(defn export-raw [{:keys [query-params identity]}] (defn export-raw [{:keys [query-params identity]}]
(assert-admin identity) (assert-admin identity)
(alog/info ::executing-query :q (get query-params "query" )) (alog/info ::executing-query :q (get query-params "query"))
(statsd/time! [(str "export.time") {:tags #{"export:raw"}}] (statsd/time! [(str "export.time") {:tags #{"export:raw"}}]
{:body {:body
(into (list) (apply dc/q (read-string (get query-params "query" )) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))})) (into (list) (apply dc/q (read-string (get query-params "query")) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
(defn export-ntg-account-snapshot [_] (defn export-ntg-account-snapshot [_]
(let [clients (->> (dc/q '[:find (pull ?e [:db/id :client/code :client/locations]) (let [clients (->> (dc/q '[:find (pull ?e [:db/id :client/code :client/locations])
@@ -564,8 +556,7 @@
{[:charge/processor :xform iol-ion.query/ident] [:db/ident]}] {[:charge/processor :xform iol-ion.query/ident] [:db/ident]}]
:sales-order/line-items [:order-line-item/item-name :sales-order/line-items [:order-line-item/item-name
:order-line-item/category :order-line-item/category
:order-line-item/total]} :order-line-item/total]}]
]
:start [:sales-order/client+date [(:db/id client) (coerce/to-date date)]] :start [:sales-order/client+date [(:db/id client) (coerce/to-date date)]]
:end [:sales-order/client+date [(:db/id client) (coerce/to-date end)]] :end [:sales-order/client+date [(:db/id client) (coerce/to-date end)]]
:reverse false :reverse false
@@ -576,40 +567,34 @@
(:db/id client)) (:db/id client))
(< (compare (:sales-order/date curr) (< (compare (:sales-order/date curr)
(coerce/to-date end)) (coerce/to-date end))
0)) 0)))))]
)))
]
entry all-entries entry all-entries
:let [sales-columns [(-> entry :sales-order/client :client/name) :let [sales-columns [(-> entry :sales-order/client :client/name)
(atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time) (atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time)
(-> entry :sales-order/total) (-> entry :sales-order/total)
(-> entry :sales-order/tip) (-> entry :sales-order/tip)
(-> entry :sales-order/service-charge) (-> entry :sales-order/service-charge)
(-> entry :sales-order/reference-link) (-> entry :sales-order/reference-link)]
]
sales-column-count (count sales-columns) sales-column-count (count sales-columns)
tender-column-count 6] tender-column-count 6]
row (concat [sales-columns] row (concat [sales-columns]
(map (fn [tender] (map (fn [tender]
(concat (concat
(take sales-column-count (repeat nil)) (take sales-column-count (repeat nil))
[ [(-> tender :charge/total)
(-> tender :charge/total)
(-> tender :charge/tax) (-> tender :charge/tax)
(-> tender :charge/tip) (-> tender :charge/tip)
(-> tender :charge/type-name) (-> tender :charge/type-name)
(some-> tender :charge/processor name) (some-> tender :charge/processor name)
(-> tender :charge/reference-link) (-> tender :charge/reference-link)]))
]))
(:sales-order/charges entry)) (:sales-order/charges entry))
(map (fn [tender] (map (fn [tender]
(concat (concat
(take (+ sales-column-count tender-column-count) (repeat nil)) (take (+ sales-column-count tender-column-count) (repeat nil))
[ [(-> tender :order-line-item/item-name)
(-> tender :order-line-item/item-name)
(-> tender :order-line-item/category) (-> tender :order-line-item/category)
(-> tender :order-line-item/total) (-> tender :order-line-item/total)]))
]))
(:sales-order/line-items entry)))] (:sales-order/line-items entry)))]
row row
@@ -617,7 +602,7 @@
_ (when balance _ (when balance
(reset! last-used-value balance)) (reset! last-used-value balance))
balance (or balance @last-used-value)] balance (or balance @last-used-value)]
[ l numeric-code (account->name a) date-str [l numeric-code (account->name a) date-str
(format "%.2f" balance)])))})) (format "%.2f" balance)])))}))
#_(export-ntg-payment-snapshot nil) #_(export-ntg-payment-snapshot nil)
@@ -628,7 +613,6 @@
(handler request) (handler request)
{:status 401}))) {:status 401})))
(def routes2 {"api/" {"sales/" {"aggregated/" {#"export/?" {:get :aggregated-sales-export}} (def routes2 {"api/" {"sales/" {"aggregated/" {#"export/?" {:get :aggregated-sales-export}}
#"export/?" {:get :export-sales} #"export/?" {:get :export-sales}
"ntg-export" {:get :export-ntg-sales-snapshot}} "ntg-export" {:get :export-ntg-sales-snapshot}}
@@ -660,10 +644,9 @@
:export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response :export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response
(wrap-schema-enforce :query-schema (mc/schema [:map (wrap-schema-enforce :query-schema (mc/schema [:map
[:date {:required true [:date {:required true
:decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]) ) :decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]))
(wrap-form-4xx-2 (fn [_] {:body "Invalid Date"})) (wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
(wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206") (wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206"))
)
:export-trial-balance (-> export-trial-balance wrap-csv-response wrap-secure) :export-trial-balance (-> export-trial-balance wrap-csv-response wrap-secure)
:export-accounts (-> export-accounts wrap-json-response wrap-secure) :export-accounts (-> export-accounts wrap-json-response wrap-secure)
:export-transactions (-> export-transactions wrap-json-response wrap-secure) :export-transactions (-> export-transactions wrap-json-response wrap-secure)

View File

@@ -23,8 +23,6 @@
:else :else
{:status 404})) {:status 404}))
(def routes {"api/" {"ezcater/" {#"event/?" :ezcater-event}}}) (def routes {"api/" {"ezcater/" {#"event/?" :ezcater-event}}})
(def match->handler {:ezcater-event (-> handle-ezcater (def match->handler {:ezcater-event (-> handle-ezcater
wrap-json-params)}) wrap-json-params)})

View File

@@ -53,8 +53,8 @@
event-date (some-> (excel/xls-date->date event-date) event-date (some-> (excel/xls-date->date event-date)
coerce/to-date-time coerce/to-date-time
atime/as-local-time atime/as-local-time
coerce/to-date )] coerce/to-date)]
(cond (and event-date client-id location ) (cond (and event-date client-id location)
[:order #:sales-order [:order #:sales-order
{:date event-date {:date event-date
:external-id (str "ezcater/order/" client-id "-" location "-" order-number) :external-id (str "ezcater/order/" client-id "-" location "-" order-number)

View File

@@ -11,9 +11,6 @@
[clojure.set :as set] [clojure.set :as set]
[datomic.api :as dc])) [datomic.api :as dc]))
(defn handle-graphql [{:keys [request-method query-params clients] :as r}] (defn handle-graphql [{:keys [request-method query-params clients] :as r}]
(when (= "none" (:user/role (:identity r))) (when (= "none" (:user/role (:identity r)))
(throw-unauthorized)) (throw-unauthorized))
@@ -26,7 +23,7 @@
{:status 200 {:status 200
:body (pr-str (ql/query (:identity r) (if (= request-method :get) (query-params "query") body) (assoc variables :body (pr-str (ql/query (:identity r) (if (= request-method :get) (query-params "query") body) (assoc variables
:clients :clients
clients) )) clients)))
:headers {"Content-Type" "application/edn"}}) :headers {"Content-Type" "application/edn"}})
(catch Throwable e (catch Throwable e
@@ -35,7 +32,7 @@
{:status 400 {:status 400
:body (pr-str result) :body (pr-str result)
:headers {"Content-Type" "application/edn"}}) :headers {"Content-Type" "application/edn"}})
(if-let [message (:validation-error (ex-data (.getCause e)) )] (if-let [message (:validation-error (ex-data (.getCause e)))]
(do (do
(alog/warn ::graphql-validation-error (alog/warn ::graphql-validation-error
:message message :message message
@@ -48,6 +45,5 @@
:body (pr-str {:errors [{:message (str "Unhandled error:" (str e))}]}) :body (pr-str {:errors [{:message (str "Unhandled error:" (str e))}]})
:headers {"Content-Type" "application/edn"}})))))) :headers {"Content-Type" "application/edn"}}))))))
(def routes {"api/" {#"graphql/?" :graphql}}) (def routes {"api/" {#"graphql/?" :graphql}})
(def match->handler {:graphql (wrap-secure handle-graphql)}) (def match->handler {:graphql (wrap-secure handle-graphql)})

View File

@@ -97,7 +97,6 @@
invoice) invoice)
(defn admin-only-if-multiple-clients [is] (defn admin-only-if-multiple-clients [is]
(let [client-count (->> is (let [client-count (->> is
(map :invoice/client) (map :invoice/client)
@@ -318,8 +317,6 @@
:data (ex-data e)}) :data (ex-data e)})
:headers {"Content-Type" "application/edn"}})))) :headers {"Content-Type" "application/edn"}}))))
(defn bulk-account-overrides [{{files :file (defn bulk-account-overrides [{{files :file
files-2 "file" files-2 "file"
client :client client :client

View File

@@ -25,8 +25,6 @@
(csv/write-csv w %) (csv/write-csv w %)
(.toString w)))))) (.toString w))))))
(defn execute-query [query-params params] (defn execute-query [query-params params]
(let [{:keys [query-id]} params] (let [{:keys [query-id]} params]
(mu/with-context {:query-id query-id} (mu/with-context {:query-id query-id}
@@ -37,7 +35,6 @@
(into (list) (apply dc/q (edn/read-string query-string) (into (list) (apply dc/q (edn/read-string query-string)
(into [(dc/db conn)] (edn/read-string (get query-params "args" "[]"))))))))) (into [(dc/db conn)] (edn/read-string (get query-params "args" "[]")))))))))
(defn put-query [guid body note & [lookup-key client]] (defn put-query [guid body note & [lookup-key client]]
(let [id (pull-id (dc/db conn) [:saved-query/lookup-key lookup-key]) (let [id (pull-id (dc/db conn) [:saved-query/lookup-key lookup-key])
guid (if lookup-key guid (if lookup-key
@@ -62,8 +59,6 @@
:csv-results-url (str "/api/queries/" guid "/results/csv") :csv-results-url (str "/api/queries/" guid "/results/csv")
:json-results-url (str "/api/queries/" guid "/results/json")}})) :json-results-url (str "/api/queries/" guid "/results/json")}}))
(defn get-queries [{:keys [identity]}] (defn get-queries [{:keys [identity]}]
(assert-admin identity) (assert-admin identity)
(let [obj (s3/list-objects :bucket-name (:data-bucket env) (let [obj (s3/list-objects :bucket-name (:data-bucket env)
@@ -77,7 +72,7 @@
(assert-admin identity) (assert-admin identity)
(put-query (str (UUID/randomUUID)) (body-string request) (query-params "note"))) (put-query (str (UUID/randomUUID)) (body-string request) (query-params "note")))
(defn get-query [{:keys [identity params]} ] (defn get-query [{:keys [identity params]}]
(assert-admin identity) (assert-admin identity)
(let [{:keys [query-id]} params (let [{:keys [query-id]} params
obj (s3/get-object :bucket-name (:data-bucket env) obj (s3/get-object :bucket-name (:data-bucket env)
@@ -89,7 +84,7 @@
:csv-results-url (str "/api/queries/" query-id "/results/csv") :csv-results-url (str "/api/queries/" query-id "/results/csv")
:json-results-url (str "/api/queries/" query-id "/results/json")}})) :json-results-url (str "/api/queries/" query-id "/results/json")}}))
(defn update-query [{:keys [query-params identity params] :as request} ] (defn update-query [{:keys [query-params identity params] :as request}]
(assert-admin identity) (assert-admin identity)
(put-query (:query-id params) (body-string request) (query-params "note"))) (put-query (:query-id params) (body-string request) (query-params "note")))

View File

@@ -22,7 +22,7 @@
{:status 200 {:status 200
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str {:token token :body (pr-str {:token token
:url (:yodlee2-fastlink env)}) })) :url (:yodlee2-fastlink env)})}))
(defn refresh-provider-accounts [{:keys [identity edn-params]}] (defn refresh-provider-accounts [{:keys [identity edn-params]}]
(assert-admin identity) (assert-admin identity)
(alog/info ::refreshing :params edn-params) (alog/info ::refreshing :params edn-params)
@@ -34,7 +34,7 @@
(:provider-account-id edn-params)) (:provider-account-id edn-params))
{:status 200 {:status 200
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body "{}" } :body "{}"}
(catch Exception e (catch Exception e
(alog/error ::error :error e) (alog/error ::error :error e)
{:status 400 {:status 400
@@ -75,7 +75,7 @@
d-clients/get-by-id d-clients/get-by-id
:client/code) :client/code)
(Long/parseLong id) (Long/parseLong id)
(dissoc data :client-id )))} (dissoc data :client-id)))}
(catch Exception e (catch Exception e
(alog/error ::error :error e) (alog/error ::error :error e)
{:status 500 {:status 500
@@ -93,7 +93,7 @@
(:provider-account-id edn-params)) (:provider-account-id edn-params))
{:status 200 {:status 200
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str {}) } :body (pr-str {})}
(catch Exception e (catch Exception e
(alog/error ::error :error e) (alog/error ::error :error e)
{:status 400 {:status 400
@@ -110,11 +110,11 @@
(def routes {"api" {"/yodlee2" {"/fastlink" :fastlink (def routes {"api" {"/yodlee2" {"/fastlink" :fastlink
"/provider-accounts/refresh/" :refresh-provider-accounts "/provider-accounts/refresh/" :refresh-provider-accounts
["/provider-accounts/" :client "/" :id ] :get-provider-account-detail ["/provider-accounts/" :client "/" :id] :get-provider-account-detail
["/reauthenticate/" :id ] :reauthenticate ["/reauthenticate/" :id] :reauthenticate
"/provider-accounts/delete/" :delete-provider-account}}}) "/provider-accounts/delete/" :delete-provider-account}}})
(def match->handler {:fastlink (-> fastlink wrap-secure (valid-for :get)) (def match->handler {:fastlink (-> fastlink wrap-secure (valid-for :get))
:refresh-provider-accounts (-> refresh-provider-accounts wrap-secure (valid-for :post)) :refresh-provider-accounts (-> refresh-provider-accounts wrap-secure (valid-for :post))
:get-provider-account-detail (-> get-provider-account-detail wrap-secure (valid-for :get)) :get-provider-account-detail (-> get-provider-account-detail wrap-secure (valid-for :get))
:reauthenticate (-> reauthenticate wrap-secure (valid-for :post)) :reauthenticate (-> reauthenticate wrap-secure (valid-for :post))
:delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))} ) :delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))})

View File

@@ -8,7 +8,7 @@
:transaction-rule/dom-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/dom-lte
:transaction-rule/amount-gte :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/amount-lte
:transaction-rule/client :transaction-rule/bank-account :transaction-rule/client :transaction-rule/bank-account
:transaction-rule/yodlee-merchant]} ] :transaction-rule/yodlee-merchant]}]
(let [transaction-dom (some-> transaction (let [transaction-dom (some-> transaction
:transaction/date :transaction/date
.toInstant .toInstant
@@ -80,8 +80,6 @@
(recur rules))) (recur rules)))
[]))) [])))
(defn group-rules-by-priority [rules] (defn group-rules-by-priority [rules]
(->> rules (->> rules
(map (fn [r] (update r :transaction-rule/description #(some-> % ->pattern)))) (map (fn [r] (update r :transaction-rule/description #(some-> % ->pattern))))
@@ -150,7 +148,7 @@
(fn [transaction valid-locations] (fn [transaction valid-locations]
(if (:transaction/payment transaction) (if (:transaction/payment transaction)
transaction transaction
(let [matching-rules (get-matching-rules-by-priority rules-by-priority transaction )] (let [matching-rules (get-matching-rules-by-priority rules-by-priority transaction)]
(if-let [top-match (and (= (count matching-rules) 1) (first matching-rules))] (if-let [top-match (and (= (count matching-rules) 1) (first matching-rules))]
(apply-rule transaction top-match valid-locations) (apply-rule transaction top-match valid-locations)
transaction)))))) transaction))))))

View File

@@ -33,7 +33,6 @@
(.addShutdownHook (Runtime/getRuntime) (.addShutdownHook (Runtime/getRuntime)
(Thread. f))) (Thread. f)))
(defn gzip-handler [] (defn gzip-handler []
(let [gz (GzipHandler.)] (let [gz (GzipHandler.)]
(doto gz (doto gz

View File

@@ -123,7 +123,6 @@
"vendor_id" (-> i :payment/vendor :db/id) "vendor_id" (-> i :payment/vendor :db/id)
"type" "payment"})) "type" "payment"}))
(defprotocol SolrClient (defprotocol SolrClient
(index-documents-raw [this index xs]) (index-documents-raw [this index xs])
(index-documents [this index xs]) (index-documents [this index xs])
@@ -158,12 +157,11 @@
(query [this index q] (query [this index q]
(-> (client/post (str (url/url solr-uri "solr" index "query")) (-> (client/post (str (url/url solr-uri "solr" index "query"))
{:body (json/write-str q ) {:body (json/write-str q)
:socket-timeout 30000 :socket-timeout 30000
:connection-timeout 30000 :connection-timeout 30000
:headers {"Content-Type" "application/json"} :headers {"Content-Type" "application/json"}
:as :json} :as :json})
)
:body :body
:response :response
:docs)) :docs))
@@ -191,11 +189,7 @@
(def impl (if (= :solr (:solr-impl env)) (def impl (if (= :solr (:solr-impl env))
(->RealSolrClient (:solr-uri env)) (->RealSolrClient (:solr-uri env))
(->MockSolrClient ))) (->MockSolrClient)))
(defn touch-with-ledger [i] (defn touch-with-ledger [i]
(index-documents impl "invoices" [i [:journal-entry/original-entity i]])) (index-documents impl "invoices" [i [:journal-entry/original-entity i]]))
@@ -205,7 +199,6 @@
([i index] ([i index]
(index-documents impl index [i]))) (index-documents impl index [i])))
(defrecord InMemSolrClient [data-set-atom] (defrecord InMemSolrClient [data-set-atom]
SolrClient SolrClient
(index-documents [this index xs] (index-documents [this index xs]

View File

@@ -27,11 +27,9 @@
"Authorization" (str "Bearer " (:client/square-auth-token client)) "Authorization" (str "Bearer " (:client/square-auth-token client))
"Content-Type" "application/json"})) "Content-Type" "application/json"}))
(defn ->square-date [d] (defn ->square-date [d]
(f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") d)) (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") d))
(def manifold-api-stream (def manifold-api-stream
(let [stream (s/stream 100)] (let [stream (s/stream 100)]
(->> stream (->> stream
@@ -104,7 +102,6 @@
:exception error)) :exception error))
[])))) []))))
(def item-cache (atom {})) (def item-cache (atom {}))
(defn fetch-catalog [client i v] (defn fetch-catalog [client i v]
@@ -124,13 +121,11 @@
#(do (swap! item-cache assoc i %) #(do (swap! item-cache assoc i %)
%)))) %))))
(defn fetch-catalog-cache [client i version] (defn fetch-catalog-cache [client i version]
(if (get @item-cache i) (if (get @item-cache i)
(de/success-deferred (get @item-cache i)) (de/success-deferred (get @item-cache i))
(fetch-catalog client i version))) (fetch-catalog client i version)))
(defn item->category-name-impl [client item version] (defn item->category-name-impl [client item version]
(capture-context->lc (capture-context->lc
(cond (:item_id (:item_variation_data item)) (cond (:item_id (:item_variation_data item))
@@ -161,7 +156,6 @@
:item item) :item item)
"Uncategorized")))) "Uncategorized"))))
(defn item-id->category-name [client i version] (defn item-id->category-name [client i version]
(capture-context->lc (capture-context->lc
(-> [client i] (-> [client i]
@@ -226,7 +220,6 @@
(concat (:orders result) continued-results)))) (concat (:orders result) continued-results))))
(:orders result))))))) (:orders result)))))))
(defn search (defn search
([client location start end] ([client location start end]
(capture-context->lc (capture-context->lc
@@ -250,11 +243,9 @@
(concat (:orders result) continued-results)))) (concat (:orders result) continued-results))))
(:orders result)))))))) (:orders result))))))))
(defn amount->money [amt] (defn amount->money [amt]
(* 0.01 (or (:amount amt) 0.0))) (* 0.01 (or (:amount amt) 0.0)))
;; to get totals: ;; to get totals:
(comment (comment
(reduce (reduce
@@ -415,7 +406,6 @@
:client client :client client
:location location))))))) :location location)))))))
(defn get-payment [client p] (defn get-payment [client p]
(de/chain (manifold-api-call (de/chain (manifold-api-call
{:url (str "https://connect.squareup.com/v2/payments/" p) {:url (str "https://connect.squareup.com/v2/payments/" p)
@@ -424,7 +414,6 @@
:body :body
:payment)) :payment))
(defn continue-payout-entry-list [c l poi cursor] (defn continue-payout-entry-list [c l poi cursor]
(capture-context->lc lc (capture-context->lc lc
(de/chain (de/chain
@@ -618,7 +607,6 @@
:count (count x)) :count (count x))
@(dc/transact-async conn x)))))))) @(dc/transact-async conn x))))))))
(defn upsert-payouts (defn upsert-payouts
([client] ([client]
(apply de/zip (apply de/zip
@@ -667,7 +655,6 @@
(log/info ::done-loading-refunds))))))) (log/info ::done-loading-refunds)))))))
(defn get-cash-shift [client id] (defn get-cash-shift [client id]
(de/chain (manifold-api-call {:url (str (url/url "https://connect.squareup.com/v2/cash-drawers/shifts" id)) (de/chain (manifold-api-call {:url (str (url/url "https://connect.squareup.com/v2/cash-drawers/shifts" id))
:method :get :method :get
@@ -826,8 +813,6 @@
d1 d1
d2)) d2))
(defn remove-voided-orders (defn remove-voided-orders
([client] ([client]
(apply de/zip (apply de/zip
@@ -865,29 +850,24 @@
@(dc/transact-async conn x))))) @(dc/transact-async conn x)))))
(de/catch (fn [e] (de/catch (fn [e]
(log/warn ::couldnt-remove :error e) (log/warn ::couldnt-remove :error e)
nil) )))))) nil)))))))
#_(comment #_(comment
(require 'auto-ap.time-reader) (require 'auto-ap.time-reader)
@(let [[c [l]] (get-square-client-and-location "DBFS") ] @(let [[c [l]] (get-square-client-and-location "DBFS")]
(log/peek :x [ c l]) (log/peek :x [c l])
(search c l #clj-time/date-time "2026-03-28" #clj-time/date-time "2026-03-29") (search c l #clj-time/date-time "2026-03-28" #clj-time/date-time "2026-03-29"))
) @(let [[c [l]] (get-square-client-and-location "NGAK")]
(log/peek :x [c l])
@(let [[c [l]] (get-square-client-and-location "NGAK") ]
(log/peek :x [ c l])
(remove-voided-orders c l #clj-time/date-time "2024-04-11" #clj-time/date-time "2024-04-15")) (remove-voided-orders c l #clj-time/date-time "2024-04-11" #clj-time/date-time "2024-04-15"))
(doseq [c (get-square-clients)] (doseq [c (get-square-clients)]
(try (try
@(remove-voided-orders c) @(remove-voided-orders c)
(catch Exception e (catch Exception e
nil))) nil))))
)
(defn upsert-all [& clients] (defn upsert-all [& clients]
(capture-context->lc (capture-context->lc
@@ -956,8 +936,6 @@
[:clients clients] [:clients clients]
@(apply upsert-all clients))) @(apply upsert-all clients)))
(comment (comment
(defn refunds-raw-cont (defn refunds-raw-cont
([client l cursor so-far] ([client l cursor so-far]
@@ -987,7 +965,6 @@
(->> (->>
@(let [[c [l]] (get-square-client-and-location "NGGG")] @(let [[c [l]] (get-square-client-and-location "NGGG")]
(search c l (time/now) (time/plus (time/now) (time/days -1)))) (search c l (time/now) (time/plus (time/now) (time/days -1))))
(filter (fn [r] (filter (fn [r]
@@ -997,7 +974,6 @@
(->> (->>
@(let [[c [l]] (get-square-client-and-location "NGGG")] @(let [[c [l]] (get-square-client-and-location "NGGG")]
(refunds-raw-cont c l nil [])) (refunds-raw-cont c l nil []))
(filter (fn [r] (filter (fn [r]
(str/starts-with? (:created_at r) "2024-03-14"))))) (str/starts-with? (:created_at r) "2024-03-14")))))
@@ -1032,12 +1008,7 @@
[(:client/code c) (atime/unparse-local (clj-time.coerce/to-date-time (:sales-order/date bad-row)) atime/normal-date) (:sales-order/total bad-row) (:sales-order/tax bad-row) (:sales-order/tip bad-row) (:db/id bad-row)]) [(:client/code c) (atime/unparse-local (clj-time.coerce/to-date-time (:sales-order/date bad-row)) atime/normal-date) (:sales-order/total bad-row) (:sales-order/tax bad-row) (:sales-order/tip bad-row) (:db/id bad-row)])
:separator \tab) :separator \tab)
;; =>
;; =>
(require 'auto-ap.time-reader) (require 'auto-ap.time-reader)
@@ -1046,26 +1017,15 @@
(clojure.pprint/pprint (let [[c [l]] (get-square-client-and-location "NGVT")] (clojure.pprint/pprint (let [[c [l]] (get-square-client-and-location "NGVT")]
l l
(def z @(search c l #clj-time/date-time "2025-02-23T00:00:00-08:00" (def z @(search c l #clj-time/date-time "2025-02-23T00:00:00-08:00"
#clj-time/date-time "2025-02-28T00:00:00-08:00")) #clj-time/date-time "2025-02-28T00:00:00-08:00"))
(take 10 (map #(first (deref (order->sales-order c l %))) z))) (take 10 (map #(first (deref (order->sales-order c l %))) z))))
)
(->> z (->> z
(filter (fn [o] (filter (fn [o]
(seq (filter (comp #{"OTHER"} :type) (:tenders o))))) (seq (filter (comp #{"OTHER"} :type) (:tenders o)))))
(filter #(not (:name (:source %)))) (filter #(not (:name (:source %))))
(count) (count))
)
(doseq [[code] (seq (dc/q '[:find ?code (doseq [[code] (seq (dc/q '[:find ?code
:in $ :in $
@@ -1075,32 +1035,22 @@
[?o :sales-order/client ?c] [?o :sales-order/client ?c]
[?c :client/code ?code]] [?c :client/code ?code]]
(dc/db conn))) (dc/db conn)))
:let [[c [l]] (get-square-client-and-location code) :let [[c [l]] (get-square-client-and-location code)]
]
order @(search c l #clj-time/date-time "2026-01-01T00:00:00-08:00" (time/now)) order @(search c l #clj-time/date-time "2026-01-01T00:00:00-08:00" (time/now))
:when (= "Invoices" (:name (:source order) )) :when (= "Invoices" (:name (:source order)))
:let [[sales-order] @(order->sales-order c l order)]] :let [[sales-order] @(order->sales-order c l order)]]
(when (should-import-order? order) (when (should-import-order? order)
(println "DATE IS" (:sales-order/date sales-order)) (println "DATE IS" (:sales-order/date sales-order))
(when (some-> (:sales-order/date sales-order) coerce/to-date-time (time/after? #clj-time/date-time "2026-2-16T00:00:00-08:00")) (when (some-> (:sales-order/date sales-order) coerce/to-date-time (time/after? #clj-time/date-time "2026-2-16T00:00:00-08:00"))
(println "WOULD UPDATE" sales-order) (println "WOULD UPDATE" sales-order)
@(dc/transact auto-ap.datomic/conn [sales-order]) @(dc/transact auto-ap.datomic/conn [sales-order]))
) #_@(dc/transact)
#_@(dc/transact ) (println "DONE")))
(println "DONE"))
)
#_(filter (comp #{"OTHER"} :type) (mapcat :tenders z)) #_(filter (comp #{"OTHER"} :type) (mapcat :tenders z))
@(let [[c [l]] (get-square-client-and-location "NGRY")] @(let [[c [l]] (get-square-client-and-location "NGRY")]
#_(search c l (clj-time.coerce/from-date #inst "2025-02-28") (clj-time.coerce/from-date #inst "2025-03-01")) #_(search c l (clj-time.coerce/from-date #inst "2025-02-28") (clj-time.coerce/from-date #inst "2025-03-01"))
(order->sales-order c l (:order (get-order c l "KdvwntmfMNTKBu8NOocbxatOs18YY" ))) (order->sales-order c l (:order (get-order c l "KdvwntmfMNTKBu8NOocbxatOs18YY")))))
)
)

View File

@@ -24,7 +24,7 @@
:account/invoice-allowance [:db/ident] :account/invoice-allowance [:db/ident]
:account/client-overrides [:db/id :account/client-overrides [:db/id
:account-client-override/name :account-client-override/name
{:account-client-override/client [:db/id :client/name]}]} ]) {:account-client-override/client [:db/id :client/name]}]}])
(defn search- [id query client] (defn search- [id query client]
(let [client-part (if (some->> client (can-see-client? id)) (let [client-part (if (some->> client (can-see-client? id))

View File

@@ -35,9 +35,8 @@
tx-instant))) tx-instant)))
(group-by (fn hours-ago [d] (group-by (fn hours-ago [d]
(time/in-hours (time/interval (coerce/to-date-time d) (time/now))) (time/in-hours (time/interval (coerce/to-date-time d) (time/now))))))]
))
)]
(for [h (range 24)] (for [h (range 24)]
(count (tx-lookup h []))))) (count (tx-lookup h [])))))
@@ -59,8 +58,7 @@
[:div [:div
[:div {:class "w-full h-64" [:div {:class "w-full h-64"
:id "client-chart" :id "client-chart"
:data-chart (hx/json { :data-chart (hx/json {:labels ["2 years ago" "1 year ago" "today"],
:labels ["2 years ago" "1 year ago" "today"],
:series [(for [n [2 1 0] :series [(for [n [2 1 0]
:let [start (time/plus (time/now) (time/years (- n)))]] :let [start (time/plus (time/now) (time/years (- n)))]]
(->> (dc/q '[:find (count ?c) (->> (dc/q '[:find (count ?c)
@@ -80,19 +78,14 @@
[:div [:div
[:div {:class "w-full h-64" [:div {:class "w-full h-64"
:id "changes" :id "changes"
:data-chart (hx/json { :data-chart (hx/json {:labels (for [n (range -24 0)]
:labels (for [n (range -24 0)]
(format "%d" n)), (format "%d" n)),
:series [(hourly-changes)]})}] :series [(hourly-changes)]})}]
[:script {:lang "javascript"} [:script {:lang "javascript"}
(hiccup/raw (hiccup/raw
"new Chartist.Line('#changes', JSON.parse(document.getElementById('changes').getAttribute('data-chart')))")]]]])] "new Chartist.Line('#changes', JSON.parse(document.getElementById('changes').getAttribute('data-chart')))")]]]])])
) "Admin"))
"Admin")
)
(def key->handler (def key->handler
{ {:auto-ap.routes.admin/page (wrap-client-redirect-unauthenticated (wrap-admin page))})
:auto-ap.routes.admin/page (wrap-client-redirect-unauthenticated (wrap-admin page))
})

View File

@@ -121,7 +121,7 @@
(some->> query-params :type) (some->> query-params :type)
(merge-query {:query {:find [] (merge-query {:query {:find []
:in ['?r] :in ['?r]
:where ['[?e :account/type ?r] ]} :where ['[?e :account/type ?r]]}
:args [(some->> query-params :type)]}) :args [(some->> query-params :type)]})
true true
@@ -213,8 +213,7 @@
(map (fn [client] (map (fn [client]
(format "'%s'" (pull-attr (dc/db conn) (format "'%s'" (pull-attr (dc/db conn)
:client/name :client/name
(-> client))) (-> client)))) %)))
) %)))
:form-params form-params)) ;; TODO shouldnt need to bubble this through. See if we can eliminate the passing of form and last-form. :form-params form-params)) ;; TODO shouldnt need to bubble this through. See if we can eliminate the passing of form and last-form.
) )
{:keys [tempids]} (audit-transact [[:upsert-entity (cond-> entity {:keys [tempids]} (audit-transact [[:upsert-entity (cond-> entity
@@ -278,14 +277,13 @@
(defn dialog* [{:keys [entity form-params form-errors]}] (defn dialog* [{:keys [entity form-params form-errors]}]
(fc/start-form form-params form-errors (fc/start-form form-params form-errors
[:div {:x-data (hx/json {"accountName" (or (:account/name form-params) (:account/numeric-code entity)) [:div {:x-data (hx/json {"accountName" (or (:account/name form-params) (:account/numeric-code entity))
"accountCode" (or (:account/numeric-code form-params) (:account/numeric-code entity) )}) "accountCode" (or (:account/numeric-code form-params) (:account/numeric-code entity))})
:hx-target "this" :hx-target "this"}
}
(com/modal (com/modal
{} {}
[:form (-> {:hx-ext "response-targets" [:form (-> {:hx-ext "response-targets"
:hx-swap "outerHTML swap:300ms" :hx-swap "outerHTML swap:300ms"
:hx-target-400 "#form-errors .error-content" } :hx-target-400 "#form-errors .error-content"}
(assoc (if (:db/id entity) (assoc (if (:db/id entity)
:hx-put :hx-put
:hx-post) :hx-post)
@@ -381,7 +379,7 @@
(com/validated-save-button {:errors (seq form-errors)} (com/validated-save-button {:errors (seq form-errors)}
"Save account")])]])])) "Save account")])]])]))
(defn new-client-override [{ {:keys [index]} :query-params}] (defn new-client-override [{{:keys [index]} :query-params}]
(html-response (html-response
(fc/start-form-with-prefix (fc/start-form-with-prefix
[:account/client-overrides (or index 0)] [:account/client-overrides (or index 0)]
@@ -416,9 +414,6 @@
{}) {})
:form-errors form-errors}))) :form-errors form-errors})))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
(->> (->>

View File

@@ -36,7 +36,6 @@
(sort-by :created-at) (sort-by :created-at)
reverse)) reverse))
(defn is-background-job? (defn is-background-job?
"This function checks whether a given task is a background job. "This function checks whether a given task is a background job.
It does this by checking the environment of the task's container definitions for an environment variable It does this by checking the environment of the task's container definitions for an environment variable
@@ -60,7 +59,7 @@
(defn job-exited-successfully? [task] (defn job-exited-successfully? [task]
(if (= 0 (->> task (if (= 0 (->> task
:containers :containers
(filter (comp #{"integreat-app" } :name)) (filter (comp #{"integreat-app"} :name))
(first) (first)
:exit-code)) :exit-code))
true true
@@ -85,7 +84,7 @@
(filter is-background-job?) (filter is-background-job?)
(map ecs-task->job))] (map ecs-task->job))]
[jobs (count jobs)])) [jobs (count jobs)]))
(def query-schema (mc/schema [:map ])) (def query-schema (mc/schema [:map]))
(def grid-page (def grid-page
(helper/build {:id "job-table" (helper/build {:id "job-table"
@@ -107,8 +106,7 @@
:entity-name "Job" :entity-name "Job"
:query-schema query-schema :query-schema query-schema
:route :admin-job-table :route :admin-job-table
:headers [ :headers [{:key "start"
{:key "start"
:name "Start" :name "Start"
:render #(some-> % :start-date (atime/unparse-local atime/standard-time))} :render #(some-> % :start-date (atime/unparse-local atime/standard-time))}
{:key "end" {:key "end"
@@ -150,7 +148,7 @@
:network-configuration {:aws-vpc-configuration {:subnets ["subnet-5e675761" "subnet-8519fde2" "subnet-89bab8d4"] :network-configuration {:aws-vpc-configuration {:subnets ["subnet-5e675761" "subnet-8519fde2" "subnet-89bab8d4"]
:security-groups ["sg-004e5855310c453a3" "sg-02d167406b1082698"] :security-groups ["sg-004e5855310c453a3" "sg-02d167406b1082698"]
:assign-public-ip AssignPublicIp/ENABLED}}} :assign-public-ip AssignPublicIp/ENABLED}}}
args (assoc-in [:overrides :container-overrides ] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}])))) args (assoc-in [:overrides :container-overrides] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}]))))
(defn job-start [{:keys [form-params]}] (defn job-start [{:keys [form-params]}]
(if (not (get (currently-running-jobs) (:name form-params))) (if (not (get (currently-running-jobs) (:name form-params)))
@@ -174,20 +172,18 @@
[:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"] [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
(com/text-input {:placeholder "ledger-data.csv" (com/text-input {:placeholder "ledger-data.csv"
:name (fc/field-name) :name (fc/field-name)
:value (fc/field-value)} )]))] :value (fc/field-value)})]))]
(= "register-invoice-import" name) (= "register-invoice-import" name)
[ [(fc/with-field :invoice-url
(fc/with-field :invoice-url
(com/validated-field {:label "Url" (com/validated-field {:label "Url"
:errors (fc/field-errors)} :errors (fc/field-errors)}
[:div.flex.place-items-center.gap-2 [:div.flex.place-items-center.gap-2
[:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"] [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
(com/text-input {:placeholder "invoice-data.csv" (com/text-input {:placeholder "invoice-data.csv"
:name (fc/field-name) :name (fc/field-name)
:value (fc/field-value)} )]))] :value (fc/field-value)})]))]
(= "load-historical-sales" name) (= "load-historical-sales" name)
[ [(fc/with-field :client
(fc/with-field :client
(com/validated-field {:label "Client" (com/validated-field {:label "Client"
:errors (fc/field-errors)} :errors (fc/field-errors)}
(com/typeahead {:name (fc/field-name) (com/typeahead {:name (fc/field-name)
@@ -200,13 +196,10 @@
:errors (fc/field-errors)} :errors (fc/field-errors)}
(com/text-input {:placeholder "60" (com/text-input {:placeholder "60"
:name (fc/field-name) :name (fc/field-name)
:value (fc/field-value)} )))] :value (fc/field-value)})))]
:else nil)) :else nil)))
(defn subform [{{:keys [name]} :query-params}]
)
(defn subform [{{:keys [name]} :query-params }]
(html-response (html-response
(fc/start-form {} nil (fc/start-form {} nil
(subform* {:name name})))) (subform* {:name name}))))
@@ -245,7 +238,7 @@
:hx-target "#sub-form" :hx-target "#sub-form"
:hx-swap "innerHTML"}))) :hx-swap "innerHTML"})))
[:div#sub-form (subform* {:name (fc/with-field :name (fc/field-value))}) ]] [:div#sub-form (subform* {:name (fc/with-field :name (fc/field-value))})]]
[:div [:div
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
@@ -256,8 +249,7 @@
[:ledger-url {:optional true} [:string {:min 1}]] [:ledger-url {:optional true} [:string {:min 1}]]
[:invoice-url {:optional true} [:string {:min 1}]] [:invoice-url {:optional true} [:string {:min 1}]]
[:client {:optional true} entity-id] [:client {:optional true} entity-id]
[:days {:optional true} [:int {:min 1 :max 120}]] [:days {:optional true} [:int {:min 1 :max 120}]]]))
]))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers

View File

@@ -47,7 +47,6 @@
(:import (:import
[java.util UUID])) [java.util UUID]))
;; TODO make more reusable malli schemas, use unions if it would be helpful ;; TODO make more reusable malli schemas, use unions if it would be helpful
;; TODO copy save logic from graphql version ;; TODO copy save logic from graphql version
;; TODO cash drawer shift ;; TODO cash drawer shift
@@ -67,8 +66,6 @@
[:enum [:enum
"" "all" "only-mine"]]]]])) "" "all" "only-mine"]]]]]))
(defn filters [request] (defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes "hx-get" (bidi/path-for ssr-routes/only-routes
@@ -178,7 +175,6 @@
:where ['[?e :client/groups ?g]]} :where ['[?e :client/groups ?g]]}
:args [(clojure.string/upper-case (:group query-params))]}) :args [(clojure.string/upper-case (:group query-params))]})
(not (str/blank? (some-> query-params :code))) (not (str/blank? (some-> query-params :code)))
(merge-query {:query {:in ['?code] (merge-query {:query {:in ['?code]
:where ['[?e :client/code ?code]]} :where ['[?e :client/code ?code]]}
@@ -293,8 +289,6 @@
(def row* (partial helper/row* grid-page)) (def row* (partial helper/row* grid-page))
(def bank-account-schema [:and [:map (def bank-account-schema [:and [:map
[:db/id [:or entity-id temp-id]] [:db/id [:or entity-id temp-id]]
[:bank-account/name :string] [:bank-account/name :string]
@@ -443,10 +437,6 @@
[:client/week-b-credits {:optional true} [:maybe :double]] [:client/week-b-credits {:optional true} [:maybe :double]]
[:client/week-b-debits {:optional true} [:maybe :double]]])) [:client/week-b-debits {:optional true} [:maybe :double]]]))
(defn email-contact-row [email-contact-cursor] (defn email-contact-row [email-contact-cursor]
(com/data-grid-row (com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? email-contact-cursor))))}) (-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? email-contact-cursor))))})
@@ -526,12 +516,10 @@
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x)))) (com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))
(defn- dialog-header [step] (defn- dialog-header [step]
[:div.flex [:div.p-2 (mm/step-name step)] [:p.ml-2.rounded.bg-gray-50.p-2.dark:bg-gray-600 [:div.flex [:div.p-2 (mm/step-name step)] [:p.ml-2.rounded.bg-gray-50.p-2.dark:bg-gray-600
[:span {:x-text "clientName"}]]]) [:span {:x-text "clientName"}]]])
(defrecord InfoModal [linear-wizard] (defrecord InfoModal [linear-wizard]
mm/ModalWizardStep mm/ModalWizardStep
(step-name [_] (step-name [_]
@@ -598,7 +586,6 @@
(mm/default-step-footer linear-wizard this :validation-route ::route/navigate) (mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
:validation-route ::route/navigate))) :validation-route ::route/navigate)))
(defn match-row [_] (defn match-row [_]
(com/data-grid-row (com/data-grid-row
{:x-ref "p" {:x-ref "p"
@@ -644,8 +631,6 @@
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))) (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(defrecord MatchesModal [linear-wizard] (defrecord MatchesModal [linear-wizard]
mm/ModalWizardStep mm/ModalWizardStep
(step-name [_] (step-name [_]
@@ -699,7 +684,6 @@
(step-key [_] (step-key [_]
:contact) :contact)
(edit-path [_ _] (edit-path [_ _]
[]) [])
@@ -798,7 +782,6 @@
:to (mm/encode-step-key [:bank-account (fc/field-value (:db/id bank-account))])})} :to (mm/encode-step-key [:bank-account (fc/field-value (:db/id bank-account))])})}
svg/pencil)]])]) svg/pencil)]])])
(defmulti bank-account-card (comp deref :bank-account/type)) (defmulti bank-account-card (comp deref :bank-account/type))
(defmethod bank-account-card :bank-account-type/cash [bank-account] (defmethod bank-account-card :bank-account-type/cash [bank-account]
(bank-account-card-base {:bg-color "bg-green-50" (bank-account-card-base {:bg-color "bg-green-50"
@@ -821,7 +804,6 @@
:icon svg/check :icon svg/check
:bank-account bank-account})) :bank-account bank-account}))
(defmulti bank-account-form (comp deref :bank-account/type)) (defmulti bank-account-form (comp deref :bank-account/type))
(defmethod bank-account-form :bank-account-type/cash [bank-account] (defmethod bank-account-form :bank-account-type/cash [bank-account]
[:div [:div
@@ -904,8 +886,6 @@
:checked (fc/field-value)} :checked (fc/field-value)}
"Visible for payment"))]]) "Visible for payment"))]])
(defn- plaid-account-select [client-id] (defn- plaid-account-select [client-id]
(fc/with-field :bank-account/plaid-account (fc/with-field :bank-account/plaid-account
(com/validated-field {:errors (fc/field-errors) (com/validated-field {:errors (fc/field-errors)
@@ -1048,7 +1028,6 @@
[:div#days-indicator [:div#days-indicator
(i/days-ago* (some-> (fc/field-value)))]]) (i/days-ago* (some-> (fc/field-value)))]])
(fc/with-field :bank-account/include-in-reports (fc/with-field :bank-account/include-in-reports
(com/checkbox {:name (fc/field-name) (com/checkbox {:name (fc/field-name)
:value (boolean (fc/field-value)) :value (boolean (fc/field-value))
@@ -1224,8 +1203,6 @@
(yodlee-account-select (:db/id (:snapshot fc/*form-data*))) (yodlee-account-select (:db/id (:snapshot fc/*form-data*)))
(intuit-account-select (:db/id (:snapshot fc/*form-data*)))]) (intuit-account-select (:db/id (:snapshot fc/*form-data*)))])
(defn new-bank-account-card [] (defn new-bank-account-card []
[:div {:class "w-[30em]"} [:div {:class "w-[30em]"}
(com/card {:class "w-full border-dotted bg-gray-50"} (com/card {:class "w-full border-dotted bg-gray-50"}
@@ -1255,7 +1232,6 @@
(edit-path [_ _] []) (edit-path [_ _] [])
(step-schema [_] (step-schema [_]
(mut/select-keys (mm/form-schema linear-wizard) #{})) (mut/select-keys (mm/form-schema linear-wizard) #{}))
@@ -1284,7 +1260,6 @@
:validation-route ::route/navigate)] :validation-route ::route/navigate)]
:validation-route ::route/navigate))) :validation-route ::route/navigate)))
(defn square-location-table [] (defn square-location-table []
[:div#square-locations [:div#square-locations
[:div.htmx-indicator [:div.htmx-indicator
@@ -1367,7 +1342,7 @@
:hx-include "#square-token" :hx-include "#square-token"
:hx-trigger "click" :hx-trigger "click"
:hx-indicator "#square-locations" :hx-indicator "#square-locations"
:hx-target "#square-locations" } :hx-target "#square-locations"}
"Refresh")] "Refresh")]
(fc/with-field :client/square-locations (fc/with-field :client/square-locations
@@ -1447,8 +1422,6 @@
(filterv #(not= (get-in multi-form-state [:step-params :db/id]) (:db/id %)) bank-accounts))) (filterv #(not= (get-in multi-form-state [:step-params :db/id]) (:db/id %)) bank-accounts)))
(mm/select-state [] nil)))) (mm/select-state [] nil))))
(defrecord CashFlowModal [linear-wizard] (defrecord CashFlowModal [linear-wizard]
mm/ModalWizardStep mm/ModalWizardStep
(step-name [_] (step-name [_]
@@ -1663,7 +1636,6 @@
#(mm/select-state % [] {}) #(mm/select-state % [] {})
#(assoc-in % [:snapshot :client/bank-accounts] new-bank-accounts))))))) #(assoc-in % [:snapshot :client/bank-accounts] new-bank-accounts)))))))
(def sales-summary-query (def sales-summary-query
"[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns) "[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns)
:with ?s :with ?s
@@ -1792,9 +1764,6 @@
[?cds :cash-drawer-shift/opened-cash ?opened-cash] [?cds :cash-drawer-shift/opened-cash ?opened-cash]
[(iol-ion.query/excel-date ?date) ?d4]]") [(iol-ion.query/excel-date ?date) ?d4]]")
(defn setup-sales-queries-impl [client-id] (defn setup-sales-queries-impl [client-id]
(let [{client-code :client/code feature-flags :client/feature-flags} (dc/pull (dc/db conn) '[:client/code :client/feature-flags] client-id) (let [{client-code :client/code feature-flags :client/feature-flags} (dc/pull (dc/db conn) '[:client/code :client/feature-flags] client-id)
is-new-square? ((set feature-flags) "new-square")] is-new-square? ((set feature-flags) "new-square")]
@@ -1840,7 +1809,6 @@
(cheshire/generate-string (format (slurp (io/resource which)) url)))} (cheshire/generate-string (format (slurp (io/resource which)) url)))}
children)) children))
(defn biweekly-sales-powerquery [request] (defn biweekly-sales-powerquery [request]
(setup-sales-queries-impl (:db/id (:route-params request))) (setup-sales-queries-impl (:db/id (:route-params request)))
(modal-response (modal-response
@@ -1872,7 +1840,6 @@
(com/modal-footer {} [:div]))))) (com/modal-footer {} [:div])))))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
{::route/page (helper/page-route grid-page) {::route/page (helper/page-route grid-page)

View File

@@ -38,7 +38,6 @@
invoice) invoice)
(defn reset-id [i] (defn reset-id [i]
(update i :invoice-number (update i :invoice-number
(fn [n] (if (re-matches #"#+" n) (fn [n] (if (re-matches #"#+" n)
@@ -85,7 +84,6 @@
(get (by (comp :db/id :vendor-schedule-payment-dom/client) :vendor-schedule-payment-dom/dom (:vendor/schedule-payment-dom vendor)) (get (by (comp :db/id :vendor-schedule-payment-dom/client) :vendor-schedule-payment-dom/dom (:vendor/schedule-payment-dom vendor))
client-id)) client-id))
(defn invoice-rows->transaction [rows user] (defn invoice-rows->transaction [rows user]
(->> rows (->> rows
(mapcat (fn [{:keys [vendor-id total client-id date invoice-number default-location check automatically-paid-when-due account-id]}] (mapcat (fn [{:keys [vendor-id total client-id date invoice-number default-location check automatically-paid-when-due account-id]}]
@@ -121,8 +119,7 @@
(let [[[bank-account]] (seq (dc/q '[:find ?ba (let [[[bank-account]] (seq (dc/q '[:find ?ba
:in $ ?c :in $ ?c
:where [?c :client/bank-accounts ?ba] :where [?c :client/bank-accounts ?ba]
[?ba :bank-account/type :bank-account-type/cash] [?ba :bank-account/type :bank-account-type/cash]]
]
(dc/db conn) (dc/db conn)
client-id))] client-id))]
[:upsert-transaction #:transaction {:amount (- (:invoice/total invoice)) [:upsert-transaction #:transaction {:amount (- (:invoice/total invoice))
@@ -130,18 +127,17 @@
:client (:invoice/client invoice) :client (:invoice/client invoice)
:status "POSTED" :status "POSTED"
:bank-account bank-account :bank-account bank-account
:db/id #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) :db/id #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
:id #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) :id #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
:raw-id transaction-id :raw-id transaction-id
:vendor (:invoice/vendor invoice) :vendor (:invoice/vendor invoice)
:description-original "Cash payment" :description-original "Cash payment"
:date (coerce/to-date date) :date (coerce/to-date date)
:approval-status :transaction-approval-status/approved :approval-status :transaction-approval-status/approved
:accounts [{:db/id (str #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) "-account") :accounts [{:db/id (str #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) "-account")
:transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"])) :transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
:transaction-account/location "A" :transaction-account/location "A"
:transaction-account/amount (Math/abs (:invoice/total invoice))}]}])) :transaction-account/amount (Math/abs (:invoice/total invoice))}]}]))]
]
[[:propose-invoice (d-invoices/code-invoice (validate-invoice (remove-nils invoice)) [[:propose-invoice (d-invoices/code-invoice (validate-invoice (remove-nils invoice))
account-id)] account-id)]
(some-> payment remove-nils) (some-> payment remove-nils)
@@ -158,10 +154,9 @@
(dc/q '[:find ?n ?v (dc/q '[:find ?n ?v
:in $ [?n ...] :in $ [?n ...]
:where [?v :vendor/name ?n]] :where [?v :vendor/name ?n]]
(dc/db conn) (dc/db conn))
)
(into {})) (into {}))
all-clients (merge (into {}(dc/q '[:find ?n (pull ?v [:db/id :client/locations]) all-clients (merge (into {} (dc/q '[:find ?n (pull ?v [:db/id :client/locations])
:in $ :in $
:where [?v :client/name ?n]] :where [?v :client/name ?n]]
(dc/db conn))) (dc/db conn)))
@@ -226,8 +221,7 @@
(com/validated-field {:label "Tab-separated invoices" (com/validated-field {:label "Tab-separated invoices"
:errors (fc/field-errors)} :errors (fc/field-errors)}
[:textarea {:class (hh/add-class "w-full h-96" inputs/default-input-classes) :placeholder (hiccup/raw sample) [:textarea {:class (hh/add-class "w-full h-96" inputs/default-input-classes) :placeholder (hiccup/raw sample)
:name (fc/field-name) :name (fc/field-name)}
}
(fc/field-value)])) (fc/field-value)]))
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
(com/validated-save-button {:color :primary (com/validated-save-button {:color :primary
@@ -275,7 +269,7 @@
"@mouseover" "show=true" "@mouseover" "show=true"
"@mouseout" "show=false" "@mouseout" "show=false"
"x-tooltip" "{content: ()=>$refs.tooltip.innerHTML , "x-tooltip" "{content: ()=>$refs.tooltip.innerHTML ,
allowHTML: true}" } allowHTML: true}"}
(format "%d vendors not found" (count (:vendors-not-found result)))) (format "%d vendors not found" (count (:vendors-not-found result))))
[:template {:x-ref "tooltip"} [:template {:x-ref "tooltip"}
@@ -309,8 +303,7 @@
::route/import (-> import ::route/import (-> import
(wrap-schema-enforce :form-schema [:map [:tsv :string]]) (wrap-schema-enforce :form-schema [:map [:tsv :string]])
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 form)) (wrap-form-4xx-2 form))})
})
(fn [h] (fn [h]
(-> h (-> h
(wrap-admin) (wrap-admin)

View File

@@ -59,7 +59,6 @@
:else :else
(pr-str v))) (pr-str v)))
(defn inspect [{{:keys [entity-id]} :params :as request}] (defn inspect [{{:keys [entity-id]} :params :as request}]
(alog/info ::inspect (alog/info ::inspect
:request request) :request request)
@@ -187,6 +186,5 @@
(if entity-id (if entity-id
(result-table {:entity-id entity-id}) (result-table {:entity-id entity-id})
[:div#history-table]) [:div#history-table])
[:div#inspector] [:div#inspector]])
])
"History"))) "History")))

View File

@@ -72,9 +72,9 @@
(defn fetch-ids [db request] (defn fetch-ids [db request]
(let [query-params (:query-params request) (let [query-params (:query-params request)
query (cond-> {:query {:find [] query (cond-> {:query {:find []
:in '[$ ] :in '[$]
:where '[]} :where '[]}
:args [db ]} :args [db]}
(:sort query-params) (add-sorter-fields {"source" ['[?e :import-batch/source ?s] (:sort query-params) (add-sorter-fields {"source" ['[?e :import-batch/source ?s]
'[?s :db/ident ?s2] '[?s :db/ident ?s2]
'[(name ?s2) ?sort-source]] '[(name ?s2) ?sort-source]]

View File

@@ -42,7 +42,7 @@
(def query-schema (mc/schema (def query-schema (mc/schema
[:maybe [:maybe
(into [:map {} (into [:map {}
[:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]] ] [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]]
default-grid-fields-schema)])) default-grid-fields-schema)]))
(defn filters [request] (defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
@@ -154,7 +154,7 @@
(not (str/blank? (:client-group query-params))) (not (str/blank? (:client-group query-params)))
(merge-query {:query {:in ['?client-group] (merge-query {:query {:in ['?client-group]
:where ['[?e :transaction-rule/client-group ?client-group] ]} :where ['[?e :transaction-rule/client-group ?client-group]]}
:args [(clojure.string/upper-case (:client-group query-params))]}) :args [(clojure.string/upper-case (:client-group query-params))]})
true true
@@ -288,10 +288,6 @@
[:transaction-rule/bank-account] [:transaction-rule/bank-account]
:form-params form-params))) :form-params form-params)))
(def transaction-read '[{:transaction/client [:client/name] (def transaction-read '[{:transaction/client [:client/name]
:transaction/bank-account [:bank-account/name]} :transaction/bank-account [:bank-account/name]}
:transaction/description-original :transaction/description-original
@@ -369,8 +365,6 @@
'[(>= ?dom ?dom-gte)]]} '[(>= ?dom ?dom-gte)]]}
:args [dom-gte]}) :args [dom-gte]})
true true
(merge-query {:query {:where ['[?e :transaction/id]]}})) (merge-query {:query {:where ['[?e :transaction/id]]}}))
results (->> results (->>
@@ -505,7 +499,6 @@
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))) (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(defn all-ids-not-locked [all-ids] (defn all-ids-not-locked [all-ids]
(->> all-ids (->> all-ids
(dc/q '[:find ?t (dc/q '[:find ?t
@@ -1003,7 +996,7 @@
{}))))}) {}))))})
(fn [h] (fn [h]
(-> h (-> h
(wrap-copy-qp-pqp) (wrap-copy-qp-pqp)
(wrap-apply-sort grid-page) (wrap-apply-sort grid-page)
(wrap-merge-prior-hx) (wrap-merge-prior-hx)
(wrap-schema-enforce :query-schema query-schema) (wrap-schema-enforce :query-schema query-schema)

View File

@@ -41,7 +41,7 @@
(into [:map {} (into [:map {}
[:name {:optional true :default nil} [:maybe [:string {:string/decode strip}]]] [:name {:optional true :default nil} [:maybe [:string {:string/decode strip}]]]
#_[:role {:optional true} [:maybe (ref->enum-schema "user-role")]] #_[:role {:optional true} [:maybe (ref->enum-schema "user-role")]]
#_[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]] ] #_[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
default-grid-fields-schema)])) default-grid-fields-schema)]))
(defn filters [request] (defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
@@ -203,8 +203,6 @@
(def row* (partial helper/row* grid-page)) (def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page)) (def table* (partial helper/table* grid-page))
(defn merge-submit [{:keys [form-params request-method identity] :as request}] (defn merge-submit [{:keys [form-params request-method identity] :as request}]
(if (= (:source-vendor form-params) (if (= (:source-vendor form-params)
(:target-vendor form-params)) (:target-vendor form-params))
@@ -245,7 +243,6 @@
(= i (dec (count steps))) (assoc :last? true)) (= i (dec (count steps))) (assoc :last? true))
n))))) n)))))
;; TODO add plaid merchant ;; TODO add plaid merchant
;; TODO each client only used once ;; TODO each client only used once
@@ -285,7 +282,6 @@
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))) (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
(defn automatically-paid-when-due-row [terms-override-cursor] (defn automatically-paid-when-due-row [terms-override-cursor]
(com/data-grid-row (com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))}) (-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))})
@@ -303,15 +299,12 @@
:value (fc/field-value) :value (fc/field-value)
:value-fn :db/id :value-fn :db/id
:content-fn #(pull-attr (dc/db conn) :client/name (:db/id %)) :content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
:size :small}))) :size :small})))
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))) (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
(defn- account-typeahead* (defn- account-typeahead*
[{:keys [name value client-id x-model]}] [{:keys [name value client-id x-model]}]
[:div.flex.flex-col [:div.flex.flex-col
@@ -370,12 +363,6 @@
(com/data-grid-cell {:class "align-top"} (com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))) (com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))))
(defn dialog* [{:keys [entity form-params form-errors] :as params}] (defn dialog* [{:keys [entity form-params form-errors] :as params}]
(alog/peek ::dialog-entity form-params) (alog/peek ::dialog-entity form-params)
(fc/start-form form-params form-errors (fc/start-form form-params form-errors
@@ -868,7 +855,6 @@
(def vendor-wizard (->VendorWizard :info)) (def vendor-wizard (->VendorWizard :info))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
(->> (->>
@@ -921,7 +907,7 @@
(fn [cursor _] (account-override-row cursor)))}) (fn [cursor _] (account-override-row cursor)))})
(fn [h] (fn [h]
(-> h (-> h
(wrap-copy-qp-pqp) (wrap-copy-qp-pqp)
(wrap-apply-sort grid-page) (wrap-apply-sort grid-page)
(wrap-merge-prior-hx) (wrap-merge-prior-hx)
(wrap-schema-enforce :query-schema query-schema) (wrap-schema-enforce :query-schema query-schema)

Some files were not shown because too many files have changed in this diff Show More