diff --git a/models/baseModels/Defaults/Defaults.ts b/models/baseModels/Defaults/Defaults.ts index e6436a2b..ca5bfc44 100644 --- a/models/baseModels/Defaults/Defaults.ts +++ b/models/baseModels/Defaults/Defaults.ts @@ -3,6 +3,10 @@ import { FiltersMap, HiddenMap } from 'fyo/model/types'; import { ModelNameEnum } from 'models/types'; export class Defaults extends Doc { + // Quick Payments + salesPaymentAccount?: string; + purchasePaymentAccount?: string; + // Number Series salesInvoiceNumberSeries?: string; purchaseInvoiceNumberSeries?: string; @@ -28,6 +32,9 @@ export class Defaults extends Doc { stockMovementPrintTemplate?: string; static commonFilters = { + // Quick Payments + salesPaymentAccount: () => ({ isGroup: false, accountType: 'Cash' }), + purchasePaymentAccount: () => ({ isGroup: false, accountType: 'Cash' }), // Number Series salesInvoiceNumberSeries: () => ({ referenceType: ModelNameEnum.SalesInvoice, diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index 6fa28955..7745ca2e 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -47,6 +47,7 @@ export abstract class Invoice extends Transactional { submitted?: boolean; cancelled?: boolean; + makeQuickPayment?: boolean; get isSales() { return this.schemaName === 'SalesInvoice'; @@ -90,6 +91,18 @@ export abstract class Invoice extends Transactional { return !this.baseGrandTotal?.eq(this.outstandingAmount!); } + get quickPaymentAccount(): string | null { + const fieldname = this.isSales + ? 'salesPaymentAccount' + : 'purchasePaymentAccount'; + const value = this.fyo.singles.Defaults?.[fieldname]; + if (typeof value === 'string' && value.length) { + return value; + } + + return null; + } + constructor(schema: Schema, data: DocValueMap, fyo: Fyo) { super(schema, data, fyo); this._setGetCurrencies(); @@ -389,6 +402,10 @@ export abstract class Invoice extends Transactional { }, dependsOn: ['items'], }, + makeQuickPayment: { + formula: () => !!this.quickPaymentAccount, + dependsOn: [], + }, }; getStockTransferred() { @@ -424,6 +441,17 @@ export abstract class Invoice extends Transactional { } hidden: HiddenMap = { + makeQuickPayment: () => { + if (this.submitted) { + return true; + } + + if (!this.quickPaymentAccount) { + return true; + } + + return false; + }, setDiscountAmount: () => true || !this.enableDiscounting, discountAmount: () => true || !(this.enableDiscounting && !!this.setDiscountAmount), @@ -446,6 +474,8 @@ export abstract class Invoice extends Transactional { }; static defaults: DefaultMap = { + makeQuickPayment: (doc) => + doc instanceof Invoice && !!doc.quickPaymentAccount, numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo), terms: (doc) => { const defaults = doc.fyo.singles.Defaults as Defaults | undefined; diff --git a/schemas/app/Defaults.json b/schemas/app/Defaults.json index 6bd9d1c3..1236bfca 100644 --- a/schemas/app/Defaults.json +++ b/schemas/app/Defaults.json @@ -4,6 +4,22 @@ "isSingle": true, "isChild": false, "fields": [ + { + "fieldname": "salesPaymentAccount", + "label": "Sales Payment Account", + "fieldtype": "Link", + "target": "Account", + "create": true, + "section": "Quick Payments" + }, + { + "fieldname": "purchasePaymentAccount", + "label": "Purchase Payment Account", + "fieldtype": "Link", + "target": "Account", + "create": true, + "section": "Quick Payments" + }, { "fieldname": "salesInvoiceNumberSeries", "label": "Sales Invoice Number Series", diff --git a/schemas/app/Invoice.json b/schemas/app/Invoice.json index b47fca77..50ca0bf2 100644 --- a/schemas/app/Invoice.json +++ b/schemas/app/Invoice.json @@ -133,12 +133,21 @@ }, { "fieldname": "discountAfterTax", - "label": "Discount After Tax", + "label": "Apply Discount After Tax", "fieldtype": "Check", "default": false, "readOnly": false, "tab": "Settings" }, + { + "fieldname": "makeQuickPayment", + "label": "Make Payment On Submit", + "fieldtype": "Check", + "computed": true, + "default": false, + "readOnly": false, + "tab": "Settings" + }, { "fieldname": "outstandingAmount", "label": "Outstanding Amount", diff --git a/src/pages/ImportWizard.vue b/src/pages/ImportWizard.vue index e407f420..e920ed12 100644 --- a/src/pages/ImportWizard.vue +++ b/src/pages/ImportWizard.vue @@ -797,7 +797,7 @@ export default defineComponent({ await showDialog({ title, type: 'error', - detail: this.t`Following cells have errors: ${cellErrors.join(', ')}`, + detail: this.t`Following cells have errors: ${cellErrors.join(', ')}.`, }); return false; } @@ -809,7 +809,7 @@ export default defineComponent({ type: 'error', detail: this.t`Following links do not exist: ${absentLinks .map((l) => `(${l.schemaLabel}, ${l.name})`) - .join(', ')}`, + .join(', ')}.`, }); return false; } @@ -927,7 +927,7 @@ export default defineComponent({ if (!isValid) { await showDialog({ title: this.t`Cannot read file`, - detail: this.t`Bad import data, could not read file`, + detail: this.t`Bad import data, could not read file.`, type: 'error', }); return; diff --git a/src/pages/SetupWizard/SetupWizard.vue b/src/pages/SetupWizard/SetupWizard.vue index b1f7bef9..960f133c 100644 --- a/src/pages/SetupWizard/SetupWizard.vue +++ b/src/pages/SetupWizard/SetupWizard.vue @@ -153,7 +153,7 @@ export default defineComponent({ if (!this.areAllValuesFilled) { return await showDialog({ title: this.t`Mandatory Error`, - detail: this.t`Please fill all values`, + detail: this.t`Please fill all values.`, type: 'error', }); } diff --git a/src/pages/TemplateBuilder/TemplateBuilder.vue b/src/pages/TemplateBuilder/TemplateBuilder.vue index 42f8c9f4..02b0b6d3 100644 --- a/src/pages/TemplateBuilder/TemplateBuilder.vue +++ b/src/pages/TemplateBuilder/TemplateBuilder.vue @@ -465,7 +465,7 @@ export default defineComponent({ await showDialog({ title: this.t`No Display Entries Found`, detail: this - .t`Please create a ${label} entry to view Template Preview`, + .t`Please create a ${label} entry to view Template Preview.`, type: 'warning', }); diff --git a/src/utils/ipcCalls.ts b/src/utils/ipcCalls.ts index af954dd1..510022d3 100644 --- a/src/utils/ipcCalls.ts +++ b/src/utils/ipcCalls.ts @@ -42,19 +42,19 @@ export async function deleteDb(filePath: string) { if (error?.code === 'EBUSY') { showDialog({ title: t`Delete Failed`, - detail: t`Please restart and try again`, + detail: t`Please restart and try again.`, type: 'error', }); } else if (error?.code === 'ENOENT') { showDialog({ title: t`Delete Failed`, - detail: t`File ${filePath} does not exist`, + detail: t`File ${filePath} does not exist.`, type: 'error', }); } else if (error?.code === 'EPERM') { showDialog({ title: t`Cannot Delete`, - detail: t`Close Frappe Books and try manually`, + detail: t`Close Frappe Books and try manually.`, type: 'error', }); } else if (error) { diff --git a/src/utils/ui.ts b/src/utils/ui.ts index 2cdfd448..c0e0141e 100644 --- a/src/utils/ui.ts +++ b/src/utils/ui.ts @@ -7,6 +7,8 @@ import type { Doc } from 'fyo/model/doc'; import { Action } from 'fyo/model/types'; import { getActions } from 'fyo/utils'; import { getDbError, LinkValidationError, ValueError } from 'fyo/utils/errors'; +import { PurchaseInvoice } from 'models/baseModels/PurchaseInvoice/PurchaseInvoice'; +import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice'; import { getLedgerLink } from 'models/helpers'; import { Transfer } from 'models/inventory/Transfer'; import { Transactional } from 'models/Transactional/Transactional'; @@ -92,7 +94,7 @@ export async function deleteDocWithPrompt(doc: Doc) { if (getDbError(err as Error) === LinkValidationError) { showDialog({ title: t`Delete Failed`, - detail: t`Cannot delete ${schemaLabel} ${doc.name!} because of linked entries.`, + detail: t`Cannot delete ${schemaLabel} "${doc.name!}" because of linked entries.`, type: 'error', }); } else { @@ -548,9 +550,9 @@ async function showSubmitOrSyncDialog(doc: Doc, type: 'submit' | 'sync') { title = t`Save ${label}?`; } - let detail = t`Mark ${doc.schema.label} as submitted`; - if (type === 'sync') { - detail = t`Save ${doc.schema.label} to database`; + let detail = t`Save ${doc.schema.label} to database.`; + if (type === 'submit') { + detail = getDocSubmitMessage(doc); } const yesAction = async () => { @@ -584,6 +586,30 @@ async function showSubmitOrSyncDialog(doc: Doc, type: 'submit' | 'sync') { }); } +function getDocSubmitMessage(doc: Doc): string { + const details = [t`Mark ${doc.schema.label} as submitted?`]; + + if (doc instanceof SalesInvoice && doc.makeQuickPayment) { + const toAccount = doc.quickPaymentAccount!; + const fromAccount = doc.account!; + const amount = fyo.format(doc.outstandingAmount, 'Currency'); + + details.push( + t`Payment of ${amount} will be made from account "${fromAccount}" to account "${toAccount}" on Submit.` + ); + } else if (doc instanceof PurchaseInvoice && doc.makeQuickPayment) { + const fromAccount = doc.quickPaymentAccount!; + const toAccount = doc.account!; + const amount = fyo.format(doc.outstandingAmount, 'Currency'); + + details.push( + t`Payment of ${amount} will be made from account "${fromAccount}" to account "${toAccount}" on Submit.` + ); + } + + return details.join(' '); +} + function showActionToast(doc: Doc, type: 'sync' | 'cancel' | 'delete') { const label = getActionLabel(doc); const message = {