Conversation
WalkthroughThe changes expand supported invoice types from three to six values, add timestamps and descriptions to invoice displays, centralize invoice type options through a helper function, update form components to use the centralized helper, and refine balance calculation logic for replenishment invoices. Changes
Sequence DiagramsequenceDiagram
participant Component as Invoice Component
participant Helper as getInvoiceTypeForSelect()
participant Service as Invoice Service
participant DB as Database
Component->>Helper: Request invoice type options
Helper-->>Component: Return array of type-label mappings
Component->>Component: Render form select with options
Note over Service: Balance Calculation Flow
Service->>DB: Fetch partner invoices
DB-->>Service: Return invoices
loop For each invoice
alt Replenishment type
alt Status is paid
Service->>Service: Add to balance
else Not paid
Service->>Service: Skip (no change)
end
else Other types
Service->>Service: Subtract from balance
end
end
Service-->>Component: Return calculated balance
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The changes span six files with mixed complexity: UI updates to InvoiceCard with new display logic and extended type mappings require careful review; service logic changes to balance calculation affect financial calculations; form refactoring to use centralized helper is straightforward. Type expansion is simple but affects multiple components. The heterogeneous nature of changes across display, form, service, and type layers warrants moderate review effort. Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
apps/web-app/shared/utils/helpers.ts (1)
4-13: Type the helper return to InvoiceType, and consider reusing a shared const arrayAdd an explicit return type so TS enforces only valid InvoiceType values. Also consider exporting a const array and returning it to avoid reallocating on each call.
Apply this minimal typing change:
-export function getInvoiceTypeForSelect() { +export function getInvoiceTypeForSelect(): { label: string; value: InvoiceType }[] {And (outside this hunk), import the type and optionally cache the options:
// add near the top import type { InvoiceType } from '@roll-stack/database' // optional: cache export const INVOICE_TYPE_OPTIONS: ReadonlyArray<{ label: string; value: InvoiceType }> = [ { label: 'Оплата роялти', value: 'royalties' }, { label: 'Паушальный взнос', value: 'lump_sum_fee' }, { label: 'Маркетинговый сбор', value: 'marketing_fee' }, { label: 'Роспатент', value: 'rospatent_fee' }, { label: 'Пополнение', value: 'replenishment' }, { label: 'Другое', value: 'other' }, ] as const export function getInvoiceTypeForSelect(): ReadonlyArray<{ label: string; value: InvoiceType }> { return INVOICE_TYPE_OPTIONS }apps/web-app/app/components/form/CreateInvoice.vue (1)
34-34: Avoid recreating options on every renderHoist the items to a const so the array isn’t rebuilt each render; reduces churn in USelect.
Template:
- :items="getInvoiceTypeForSelect()" + :items="invoiceTypeItems"Script (outside this hunk):
const invoiceTypeItems = getInvoiceTypeForSelect()apps/web-app/app/components/form/UpdateInvoice.vue (1)
51-51: Hoist select options to avoid per-render allocationsMirror CreateInvoice.vue: compute once.
Template:
- :items="getInvoiceTypeForSelect()" + :items="invoiceTypeItems"Script (outside this hunk):
const invoiceTypeItems = getInvoiceTypeForSelect()apps/web-app/app/components/InvoiceCard.vue (2)
68-77: Deduplicate type label mapping; derive from a single sourceType labels here can drift from
getInvoiceTypeForSelect(). Centralize via a shared label map/helper and reuse in both places.Example (in helpers.ts):
export const invoiceTypeLabelMap: Record<Invoice['type'], string> = { replenishment: 'Пополнение', royalties: 'Роялти', lump_sum_fee: 'Паушальный взнос', marketing_fee: 'Маркетинговый сбор', rospatent_fee: 'Роспатент', other: 'Другое', } export const getInvoiceTypeLabel = (t: Invoice['type']) => invoiceTypeLabelMap[t] ?? 'Другое'Then here:
function getInfoByType(type: Invoice['type']) { return getInvoiceTypeLabel(type) }
17-19: Guard formatting if createdAt can be null/invalidIf any legacy invoices lack
createdAt, render a fallback to avoidInvalid Date.<div class="text-sm/4 text-muted"> {{ invoice.createdAt ? `Создан ${format(new Date(invoice.createdAt), 'd MMMM в HH:mm', { locale: ru })}` : 'Дата неизвестна' }} </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/web-app/app/components/InvoiceCard.vue(7 hunks)apps/web-app/app/components/form/CreateInvoice.vue(2 hunks)apps/web-app/app/components/form/UpdateInvoice.vue(2 hunks)apps/web-app/app/pages/partner/[id]/invoice.vue(2 hunks)apps/web-app/server/services/invoice.ts(1 hunks)apps/web-app/shared/utils/helpers.ts(1 hunks)packages/database/src/types/entities.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (1)
apps/web-app/server/services/invoice.ts (1)
8-13: Confirm business rule: subtract all non‑replenishment invoices regardless of statusCurrent logic:
- Replenishment: add only when status === 'paid'.
- All others: always subtract.
Is it intended that unpaid expense invoices reduce balance immediately (i.e., balance reflects liabilities), while unpaid replenishments don’t increase it? If yes, consider a brief comment and a unit test to lock this in. If not, gate the subtraction on status.
If subtraction should apply only when unpaid:
- // All other invoices - balance -= invoice.total + // All other invoices (count only unpaid as liability) + if (invoice.status === 'unpaid') { + balance -= invoice.total + }Also applies to: 16-17
| import { format } from 'date-fns' | ||
| import { ru } from 'date-fns/locale/ru' | ||
|
|
There was a problem hiding this comment.
Fix date‑fns locale import to avoid runtime error
date-fns/locale/ru exports default, not a named ru. Use default import.
-import { format } from 'date-fns'
-import { ru } from 'date-fns/locale/ru'
+import { format } from 'date-fns'
+import ru from 'date-fns/locale/ru'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { format } from 'date-fns' | |
| import { ru } from 'date-fns/locale/ru' | |
| import { format } from 'date-fns' | |
| import ru from 'date-fns/locale/ru' |
🤖 Prompt for AI Agents
In apps/web-app/app/components/InvoiceCard.vue around lines 55 to 57, the
date-fns locale is imported as a named export (`{ ru }`) but date-fns/locale/ru
provides a default export; change the import to use the default (`import ru from
'date-fns/locale/ru'`) and update any references that expect a named `ru` to use
the default `ru` variable so the locale is passed correctly to format functions
to avoid the runtime error.
|
|
||
| <InvoiceCard | ||
| v-for="invoice in activeInvoices" | ||
| v-for="invoice in invoices" |
There was a problem hiding this comment.
Fix potential crash and compatibility: optional chain + avoid toSorted
- If
partneris undefined,partner.value?.invoices.toSorted(...)callstoSortedon undefined → runtime error. Array.prototype.toSortedis ES2023; SSR/runtime may not support it.
Use a safe fallback and compatible sort:
-const invoices = computed(() => partner.value?.invoices.toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()))
+const invoices = computed(() =>
+ (partner.value?.invoices ?? [])
+ .slice() // clone to avoid mutating store
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
+)Also applies to: 31-31
🤖 Prompt for AI Agents
In apps/web-app/app/pages/partner/[id]/invoice.vue around lines 7 and 31, the
current expressions call partner.value?.invoices.toSorted(...) which can throw
if partner or invoices is undefined and also uses the ES2023
Array.prototype.toSorted API which may not be available at SSR/runtime; change
both occurrences to safely fall back to an empty array and use a compatible sort
such as [...(partner.value?.invoices ?? [])].sort((a,b) => /* comparator */) or
(partner.value?.invoices ?? []).slice().sort((a,b) => /* comparator */) so you
never call methods on undefined and avoid toSorted. Ensure the comparator
replicates the original sort logic.
| export type InvoiceType = 'replenishment' | ||
| | 'royalties' // Роялти | ||
| | 'lump_sum_fee' // Паушальный взнос | ||
| | 'marketing_fee' // Маркетинговый сбор | ||
| | 'rospatent_fee' // Роспатент | ||
| | 'other' | ||
| export type InvoiceStatus = 'paid' | 'unpaid' |
There was a problem hiding this comment.
🧩 Analysis chain
Ensure validators/DB constraints accept the new InvoiceType values
With new literals added, please verify:
- DB enum/check constraints (if any) and migrations include these values.
- API/Zod/DTO validators and UI filters handle them.
Run to find hard‑coded checks that may need updates:
🏁 Script executed:
#!/bin/bash
# Find InvoiceType usages and literal comparisons
rg -nP -C2 "\bInvoiceType\b|invoice\.type\s*[!=]==?\s*['\"][a-z_]+['\"]|['\"](lump_sum_fee|marketing_fee|rospatent_fee)['\"]"Length of output: 3466
🏁 Script executed:
#!/bin/bash
# Find DB migrations, enum definitions, and validators
echo "=== DB Migrations and Enum Definitions ==="
fd -t f -e sql -e ts -e js -path "*migration*" . 2>/dev/null | head -20
echo -e "\n=== Zod Validators and Schema Definitions ==="
rg -l "ZodType|z\.enum|z\.literal|validator|schema" --type=ts --type=js packages/ apps/ 2>/dev/null | grep -E "(validator|schema|zod)" | head -20
echo -e "\n=== Enum definitions in database package ==="
rg -nP "enum.*Invoice|InvoiceType.*=" packages/database/ --type=ts
echo -e "\n=== Check constraints in table definitions ==="
rg -nP -A2 -B2 "type.*varchar.*InvoiceType|\.check\(" packages/database/src/tables.tsLength of output: 314
🏁 Script executed:
#!/bin/bash
# Find DB migrations, enum definitions, and validators
echo "=== DB Migrations and SQL Definitions ==="
find . -type f \( -name "*migration*" -o -name "*schema*" \) 2>/dev/null | head -20
echo -e "\n=== Zod Validators in packages ==="
rg -l "z\.enum|z\.literal|validator" --type=ts --type=js 2>/dev/null | grep -v node_modules | head -20
echo -e "\n=== Enum or const for InvoiceType ==="
rg -nP "(enum|const).*Invoice" --type=ts 2>/dev/null
echo -e "\n=== DB Table definition with check constraints ==="
rg -nP -A5 "invoices.*table|type.*varchar.*Invoice" packages/database/src/ --type=ts 2>/dev/null | head -30Length of output: 3065
🏁 Script executed:
#!/bin/bash
# View the Zod schema definitions for invoices
echo "=== Zod Schema for createPartnerInvoiceSchema ==="
sed -n '55,75p' apps/web-app/shared/services/partner.ts
echo -e "\n=== Zod Schema for updatePartnerInvoiceSchema ==="
sed -n '64,80p' apps/web-app/shared/services/partner.ts
echo -e "\n=== Search for migrations in database package ==="
find packages/database -type f \( -name "*.sql" -o -name "*.ts" -o -name "*.js" \) 2>/dev/null | grep -i migrat
echo -e "\n=== Check for enum() or check constraints in tables.ts ==="
rg -nP "enum\(|\.check\(|values\(" packages/database/src/tables.ts
echo -e "\n=== Full context around invoice table definition ==="
sed -n '790,800p' packages/database/src/tables.tsLength of output: 2324
🏁 Script executed:
#!/bin/bash
# Check if type() function has enum validation or if used with enum values
echo "=== Definition of type() helper function ==="
rg -nP "export.*const type|function type" apps/web-app/shared/ --type=ts | head -5
echo -e "\n=== Check partner.ts imports ==="
head -20 apps/web-app/shared/services/partner.ts
echo -e "\n=== Search for any InvoiceType validation in routes ==="
rg -nP "InvoiceType|invoiceType" apps/web-app/server/api/ --type=ts
echo -e "\n=== Check for database schema constraints in SQL or migrations ==="
find packages/database -type f -name "*.sql" 2>/dev/nullLength of output: 1317
🏁 Script executed:
#!/bin/bash
# Verify arktype supports literal constraints for enums
echo "=== Full createPartnerInvoiceSchema definition ==="
rg -nP -A15 "export const createPartnerInvoiceSchema" apps/web-app/shared/services/partner.ts
echo -e "\n=== Search for arktype examples with literals in codebase ==="
rg -nP "type\(['\"](.*\s*\|\s*.*)['\"]" apps/web-app/shared/services/ --type=ts | head -10
echo -e "\n=== Check if any schema uses enum patterns ==="
rg -nP "'\w+'\s*\|" apps/web-app/shared/services/partner.ts | head -10Length of output: 2415
Update Arktype validators to enforce new InvoiceType values
The invoice schemas in apps/web-app/shared/services/partner.ts use generic string validation and will not enforce the new InvoiceType literals. The codebase already demonstrates the correct pattern in task.ts (line 3) with literal unions.
Required fixes:
- Line 59: Change
type: type('string')totype: type('replenishment' | 'royalties' | 'lump_sum_fee' | 'marketing_fee' | 'rospatent_fee' | 'other') - Line 60: Change
status: type('string')tostatus: type('paid' | 'unpaid') - Line 68: Change
type: type('string | undefined')totype: type('replenishment' | 'royalties' | 'lump_sum_fee' | 'marketing_fee' | 'rospatent_fee' | 'other' | undefined) - Line 69: Change
status: type('string | undefined')tostatus: type('paid' | 'unpaid' | undefined)
Additionally, no database-level check constraints were found. Consider adding Drizzle check constraints to the invoices table definition as a secondary validation layer.
🤖 Prompt for AI Agents
packages/database/src/types/entities.ts lines 100-106: The Arktype validators in
apps/web-app/shared/services/partner.ts currently use generic 'string' types
which won't enforce the InvoiceType/InvoiceStatus literal unions; update the
validator calls as follows: change type('string') to type('replenishment' |
'royalties' | 'lump_sum_fee' | 'marketing_fee' | 'rospatent_fee' | 'other') and
type('string') for status to type('paid' | 'unpaid'); for optional fields change
type('string | undefined') to type('replenishment' | 'royalties' |
'lump_sum_fee' | 'marketing_fee' | 'rospatent_fee' | 'other' | undefined) and
type('string | undefined') for status to type('paid' | 'unpaid' | undefined);
also consider adding Drizzle check constraints on the invoices table to enforce
these enums at the DB level as a secondary validation layer.



Summary by CodeRabbit
Release Notes
New Features
Improvements