2
0
mirror of https://github.com/frappe/books.git synced 2025-02-03 04:28:32 +00:00

Merge pull request #1071 from frappe/payment-methods

feat: payment methods
This commit is contained in:
Akshay 2024-12-27 17:01:59 +05:30 committed by GitHub
commit a37bee8086
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 289 additions and 150 deletions

View File

@ -402,10 +402,7 @@ export class BespokeQueries {
const sinvNamesQuery = db.knex!(ModelNameEnum.SalesInvoice) const sinvNamesQuery = db.knex!(ModelNameEnum.SalesInvoice)
.select('name') .select('name')
.where('isPOS', true) .where('isPOS', true)
.andWhereBetween('date', [ .andWhereBetween('date', [fromDate.toISOString(), toDate.toISOString()]);
DateTime.fromJSDate(fromDate).toSQLDate(),
DateTime.fromJSDate(toDate).toSQLDate(),
]);
if (lastShiftClosingDate) { if (lastShiftClosingDate) {
sinvNamesQuery.andWhere( sinvNamesQuery.andWhere(

View File

@ -0,0 +1,57 @@
import { ModelNameEnum } from 'models/types';
import { DatabaseManager } from '../database/manager';
import { AccountTypeEnum } from 'models/baseModels/Account/types';
import { getDefaultMetaFieldValueMap } from 'backend/helpers';
type AccountTypeMap = Record<AccountTypeEnum, string[] | undefined>;
async function execute(dm: DatabaseManager) {
const accounts = (await dm.db?.getAll(ModelNameEnum.Account, {
fields: ['name', 'accountType'],
filters: {
accountType: [
'in',
[
AccountTypeEnum.Bank,
AccountTypeEnum.Cash,
AccountTypeEnum.Payable,
AccountTypeEnum.Receivable,
],
],
},
})) as { name: string; accountType: AccountTypeEnum }[];
const accountsMap = accounts.reduce((acc, ac) => {
acc[ac.accountType] ??= [];
acc[ac.accountType]!.push(ac.name);
return acc;
}, {} as AccountTypeMap);
const defaults = getDefaultMetaFieldValueMap();
const paymentMethods = [
{
name: 'Cash',
type: 'Cash',
account: accountsMap[AccountTypeEnum.Cash]?.[0],
...defaults,
},
{
name: 'Bank',
type: 'Bank',
account: accountsMap[AccountTypeEnum.Bank]?.[0],
...defaults,
},
{
name: 'Transfer',
type: 'Bank',
account: accountsMap[AccountTypeEnum.Bank]?.[0],
...defaults,
},
];
for (const paymentMethod of paymentMethods) {
await dm.db?.insert(ModelNameEnum.PaymentMethod, paymentMethod);
}
}
export default { execute };

View File

@ -7,6 +7,7 @@ import updateSchemas from './updateSchemas';
import setPaymentReferenceType from './setPaymentReferenceType'; import setPaymentReferenceType from './setPaymentReferenceType';
import fixLedgerDateTime from './v0_21_0/fixLedgerDateTime'; import fixLedgerDateTime from './v0_21_0/fixLedgerDateTime';
import fixItemHSNField from './fixItemHSNField'; import fixItemHSNField from './fixItemHSNField';
import createPaymentMethods from './createPaymentMethods';
export default [ export default [
{ name: 'testPatch', version: '0.5.0-beta.0', patch: testPatch }, { name: 'testPatch', version: '0.5.0-beta.0', patch: testPatch },
@ -42,4 +43,9 @@ export default [
patch: fixLedgerDateTime, patch: fixLedgerDateTime,
}, },
{ name: 'fixItemHSNField', version: '0.24.0', patch: fixItemHSNField }, { name: 'fixItemHSNField', version: '0.24.0', patch: fixItemHSNField },
{
name: 'createPaymentMethods',
version: '0.25.1',
patch: createPaymentMethods,
},
] as Patch[]; ] as Patch[];

View File

@ -9,7 +9,6 @@ import {
FormulaMap, FormulaMap,
HiddenMap, HiddenMap,
ListViewSettings, ListViewSettings,
RequiredMap,
ValidationMap, ValidationMap,
} from 'fyo/model/types'; } from 'fyo/model/types';
import { NotFoundError, ValidationError } from 'fyo/utils/errors'; import { NotFoundError, ValidationError } from 'fyo/utils/errors';
@ -27,8 +26,9 @@ import { AccountTypeEnum } from '../Account/types';
import { Invoice } from '../Invoice/Invoice'; import { Invoice } from '../Invoice/Invoice';
import { Party } from '../Party/Party'; import { Party } from '../Party/Party';
import { PaymentFor } from '../PaymentFor/PaymentFor'; import { PaymentFor } from '../PaymentFor/PaymentFor';
import { PaymentMethod, PaymentType } from './types'; import { PaymentType } from './types';
import { TaxSummary } from '../TaxSummary/TaxSummary'; import { TaxSummary } from '../TaxSummary/TaxSummary';
import { PaymentMethod } from '../PaymentMethod/PaymentMethod';
type AccountTypeMap = Record<AccountTypeEnum, string[] | undefined>; type AccountTypeMap = Record<AccountTypeEnum, string[] | undefined>;
@ -38,10 +38,15 @@ export class Payment extends Transactional {
amount?: Money; amount?: Money;
writeoff?: Money; writeoff?: Money;
paymentType?: PaymentType; paymentType?: PaymentType;
paymentMethod?: string;
referenceType?: ModelNameEnum.SalesInvoice | ModelNameEnum.PurchaseInvoice; referenceType?: ModelNameEnum.SalesInvoice | ModelNameEnum.PurchaseInvoice;
for?: PaymentFor[]; for?: PaymentFor[];
_accountsMap?: AccountTypeMap; _accountsMap?: AccountTypeMap;
async paymentMethodDoc() {
return (await this.loadAndGetLink('paymentMethod')) as PaymentMethod;
}
async change({ changed }: ChangeArg) { async change({ changed }: ChangeArg) {
if (changed === 'for') { if (changed === 'for') {
this.updateAmountOnReferenceUpdate(); this.updateAmountOnReferenceUpdate();
@ -110,6 +115,7 @@ export class Payment extends Transactional {
this.validateAccounts(); this.validateAccounts();
this.validateTotalReferenceAmount(); this.validateTotalReferenceAmount();
await this.validateReferences(); await this.validateReferences();
await this.validateReferencesAreSet();
} }
async validateFor() { async validateFor() {
@ -223,6 +229,22 @@ export class Payment extends Transactional {
); );
} }
async validateReferencesAreSet() {
const type = (await this.paymentMethodDoc()).type;
if (type !== 'Bank') {
return;
}
if (!this.clearanceDate) {
throw new ValidationError(t`Clearance Date not set.`);
}
if (!this.referenceId) {
throw new ValidationError(t`Reference Id not set.`);
}
}
async getTaxSummary() { async getTaxSummary() {
const taxes: Record< const taxes: Record<
string, string,
@ -559,15 +581,13 @@ export class Payment extends Transactional {
); );
} }
if (this.paymentMethod === 'Cash') { const paymentMethodDoc = await this.paymentMethodDoc();
if (paymentMethodDoc.type === 'Cash') {
return accountsMap[AccountTypeEnum.Cash]?.[0] ?? null; return accountsMap[AccountTypeEnum.Cash]?.[0] ?? null;
} }
if (this.paymentMethod !== 'Cash') { return accountsMap[AccountTypeEnum.Bank]?.[0] ?? null;
return accountsMap[AccountTypeEnum.Bank]?.[0] ?? null;
}
return null;
}, },
dependsOn: ['paymentMethod', 'paymentType', 'party'], dependsOn: ['paymentMethod', 'paymentType', 'party'],
}, },
@ -582,15 +602,17 @@ export class Payment extends Transactional {
); );
} }
if (this.paymentMethod === 'Cash') { const paymentMethodDoc = await this.paymentMethodDoc();
if (paymentMethodDoc.account) {
return paymentMethodDoc.get('account');
}
if (paymentMethodDoc.type === 'Cash') {
return accountsMap[AccountTypeEnum.Cash]?.[0] ?? null; return accountsMap[AccountTypeEnum.Cash]?.[0] ?? null;
} }
if (this.paymentMethod !== 'Cash') { return accountsMap[AccountTypeEnum.Bank]?.[0] ?? null;
return accountsMap[AccountTypeEnum.Bank]?.[0] ?? null;
}
return null;
}, },
dependsOn: ['paymentMethod', 'paymentType', 'party'], dependsOn: ['paymentMethod', 'paymentType', 'party'],
}, },
@ -673,14 +695,7 @@ export class Payment extends Transactional {
}, },
}; };
required: RequiredMap = {
referenceId: () => this.paymentMethod !== 'Cash',
clearanceDate: () => this.paymentMethod !== 'Cash',
};
hidden: HiddenMap = { hidden: HiddenMap = {
referenceId: () => this.paymentMethod === 'Cash',
clearanceDate: () => this.paymentMethod === 'Cash',
amountPaid: () => this.writeoff?.isZero() ?? true, amountPaid: () => this.writeoff?.isZero() ?? true,
attachment: () => attachment: () =>
!(this.attachment || !(this.isSubmitted || this.isCancelled)), !(this.attachment || !(this.isSubmitted || this.isCancelled)),
@ -712,7 +727,7 @@ export class Payment extends Transactional {
return { accountType: 'Receivable', isGroup: false }; return { accountType: 'Receivable', isGroup: false };
} }
if (paymentMethod === 'Cash') { if (paymentMethod.name === 'Cash') {
return { accountType: 'Cash', isGroup: false }; return { accountType: 'Cash', isGroup: false };
} else { } else {
return { accountType: ['in', ['Bank', 'Cash']], isGroup: false }; return { accountType: ['in', ['Bank', 'Cash']], isGroup: false };
@ -726,7 +741,7 @@ export class Payment extends Transactional {
return { accountType: 'Payable', isGroup: false }; return { accountType: 'Payable', isGroup: false };
} }
if (paymentMethod === 'Cash') { if (paymentMethod.name === 'Cash') {
return { accountType: 'Cash', isGroup: false }; return { accountType: 'Cash', isGroup: false };
} else { } else {
return { accountType: ['in', ['Bank', 'Cash']], isGroup: false }; return { accountType: ['in', ['Bank', 'Cash']], isGroup: false };

View File

@ -0,0 +1,16 @@
import { Doc } from 'fyo/model/doc';
import { Account } from '../Account/Account';
import { ListViewSettings } from 'fyo/model/types';
import { PaymentMethodType } from 'models/types';
export class PaymentMethod extends Doc {
name?: string;
account?: Account;
type?: PaymentMethodType;
static getListViewSettings(): ListViewSettings {
return {
columns: ['name', 'type'],
};
}
}

View File

@ -16,6 +16,7 @@ import { Lead } from './baseModels/Lead/Lead';
import { AppliedCouponCodes } from './baseModels/AppliedCouponCodes/AppliedCouponCodes'; import { AppliedCouponCodes } from './baseModels/AppliedCouponCodes/AppliedCouponCodes';
import { CouponCode } from './baseModels/CouponCode/CouponCode'; import { CouponCode } from './baseModels/CouponCode/CouponCode';
import { Payment } from './baseModels/Payment/Payment'; import { Payment } from './baseModels/Payment/Payment';
import { PaymentMethod } from './baseModels/PaymentMethod/PaymentMethod';
import { PaymentFor } from './baseModels/PaymentFor/PaymentFor'; import { PaymentFor } from './baseModels/PaymentFor/PaymentFor';
import { PriceList } from './baseModels/PriceList/PriceList'; import { PriceList } from './baseModels/PriceList/PriceList';
import { PriceListItem } from './baseModels/PriceList/PriceListItem'; import { PriceListItem } from './baseModels/PriceList/PriceListItem';
@ -69,6 +70,7 @@ export const models = {
CollectionRulesItems, CollectionRulesItems,
CouponCode, CouponCode,
Payment, Payment,
PaymentMethod,
PaymentFor, PaymentFor,
PrintSettings, PrintSettings,
PriceList, PriceList,

View File

@ -26,6 +26,7 @@ export enum ModelNameEnum {
AppliedCouponCodes = 'AppliedCouponCodes', AppliedCouponCodes = 'AppliedCouponCodes',
Payment = 'Payment', Payment = 'Payment',
PaymentMethod = 'PaymentMethod',
PaymentFor = 'PaymentFor', PaymentFor = 'PaymentFor',
PriceList = 'PriceList', PriceList = 'PriceList',
PricingRule = 'PricingRule', PricingRule = 'PricingRule',
@ -68,3 +69,5 @@ export enum ModelNameEnum {
} }
export type ModelName = keyof typeof ModelNameEnum; export type ModelName = keyof typeof ModelNameEnum;
export type PaymentMethodType= 'Cash' | 'Bank'

View File

@ -81,23 +81,11 @@
"fieldname": "paymentMethod", "fieldname": "paymentMethod",
"label": "Payment Method", "label": "Payment Method",
"placeholder": "Payment Method", "placeholder": "Payment Method",
"fieldtype": "Select", "fieldtype": "Link",
"options": [ "target": "PaymentMethod",
{
"value": "Cash",
"label": "Cash"
},
{
"value": "Cheque",
"label": "Cheque"
},
{
"value": "Transfer",
"label": "Transfer"
}
],
"default": "Cash", "default": "Cash",
"required": true, "required": true,
"create": true,
"section": "Details" "section": "Details"
}, },
{ {

View File

@ -0,0 +1,35 @@
{
"name": "PaymentMethod",
"label": "Payment Method",
"naming": "manual",
"fields": [
{
"fieldname": "name",
"label": "Name",
"fieldtype": "Data"
},
{
"fieldname": "type",
"label": "Type",
"fieldtype": "Select",
"required": true,
"options": [
{
"value": "Cash",
"label": "Cash"
},
{
"value": "Bank",
"label": "Bank"
}
]
},
{
"fieldname": "account",
"label": "Account",
"fieldtype": "Link",
"target": "Account"
}
],
"quickEditFields": ["name", "type", "account"]
}

View File

@ -22,6 +22,7 @@ import CollectionRulesItems from './app/CollectionRulesItems.json';
import CouponCode from './app/CouponCode.json'; import CouponCode from './app/CouponCode.json';
import AppliedCouponCodes from './app/AppliedCouponCodes.json'; import AppliedCouponCodes from './app/AppliedCouponCodes.json';
import Payment from './app/Payment.json'; import Payment from './app/Payment.json';
import PaymentMethod from './app/PaymentMethod.json';
import PaymentFor from './app/PaymentFor.json'; import PaymentFor from './app/PaymentFor.json';
import PriceList from './app/PriceList.json'; import PriceList from './app/PriceList.json';
import PriceListItem from './app/PriceListItem.json'; import PriceListItem from './app/PriceListItem.json';
@ -117,6 +118,7 @@ export const appSchemas: Schema[] | SchemaStub[] = [
CollectionRulesItems as Schema, CollectionRulesItems as Schema,
Payment as Schema, Payment as Schema,
PaymentMethod as Schema,
PaymentFor as Schema, PaymentFor as Schema,
JournalEntry as Schema, JournalEntry as Schema,

View File

@ -25,7 +25,8 @@ export type PosEmits =
| 'addItem' | 'addItem'
| 'toggleView' | 'toggleView'
| 'toggleModal' | 'toggleModal'
| 'setCashAmount' | 'setPaidAmount'
| 'setPaymentMethod'
| 'setCouponsCount' | 'setCouponsCount'
| 'routeToSinvList' | 'routeToSinvList'
| 'applyPricingRule' | 'applyPricingRule'

View File

@ -43,9 +43,11 @@
<PaymentModal <PaymentModal
:open-modal="openPaymentModal" :open-modal="openPaymentModal"
@toggle-modal="emitEvent('toggleModal', 'Payment')" @toggle-modal="emitEvent('toggleModal', 'Payment')"
@set-cash-amount="(amount) => emitEvent('setCashAmount', amount)" @set-paid-amount="(amount: Money) => emitEvent('setPaidAmount', amount)"
@set-payment-method="
(paymentMethod) => emitEvent('setPaymentMethod', paymentMethod)
"
@set-transfer-ref-no="(ref) => emitEvent('setTransferRefNo', ref)" @set-transfer-ref-no="(ref) => emitEvent('setTransferRefNo', ref)"
@set-transfer-amount="(amount) => emitEvent('setTransferAmount', amount)"
@set-transfer-clearance-date=" @set-transfer-clearance-date="
(date) => emitEvent('setTransferClearanceDate', date) (date) => emitEvent('setTransferClearanceDate', date)
" "
@ -338,7 +340,7 @@ export default defineComponent({
FloatingLabelCurrencyInput, FloatingLabelCurrencyInput,
}, },
props: { props: {
cashAmount: Money, paidAmount: Money,
tableView: Boolean, tableView: Boolean,
itemDiscounts: Money, itemDiscounts: Money,
openAlertModal: Boolean, openAlertModal: Boolean,
@ -390,9 +392,10 @@ export default defineComponent({
'toggleModal', 'toggleModal',
'setCustomer', 'setCustomer',
'clearValues', 'clearValues',
'setCashAmount', 'setPaidAmount',
'setCouponsCount', 'setCouponsCount',
'routeToSinvList', 'routeToSinvList',
'setPaymentMethod',
'setTransferRefNo', 'setTransferRefNo',
'setLoyaltyPoints', 'setLoyaltyPoints',
'applyPricingRule', 'applyPricingRule',
@ -409,7 +412,10 @@ export default defineComponent({
}; };
}, },
methods: { methods: {
emitEvent(eventName: PosEmits, ...args: (string | boolean | Item)[]) { emitEvent(
eventName: PosEmits,
...args: (string | boolean | Item | Money)[]
) {
this.$emit(eventName, ...args); this.$emit(eventName, ...args);
}, },
getItem, getItem,

View File

@ -121,14 +121,16 @@ export default defineComponent({
}, },
methods: { methods: {
async setTransactedAmount() { async setTransactedAmount() {
if (!fyo.singles.POSShift?.openingDate) { this.posOpeningShiftDoc = await getPOSOpeningShiftDoc(fyo);
const fromDate = this.posOpeningShiftDoc?.openingDate as Date;
if (!fromDate) {
return; return;
} }
const fromDate = this.posOpeningShiftDoc?.openingDate as Date;
this.transactedAmount = await fyo.db.getPOSTransactedAmount( this.transactedAmount = await fyo.db.getPOSTransactedAmount(
fromDate, fromDate,
new Date(), new Date()
fyo.singles.POSShift.closingDate as Date
); );
}, },
seedClosingCash() { seedClosingCash() {
@ -160,19 +162,7 @@ export default defineComponent({
return; return;
} }
let expectedAmount = fyo.pesa(0); let expectedAmount = row.amount ?? fyo.pesa(0);
if (row.paymentMethod === 'Cash') {
expectedAmount = expectedAmount.add(
this.posOpeningShiftDoc?.openingCashAmount as Money
);
}
if (row.paymentMethod === 'Transfer') {
expectedAmount = expectedAmount.add(
this.posOpeningShiftDoc?.openingTransferAmount as Money
);
}
if (this.transactedAmount) { if (this.transactedAmount) {
expectedAmount = expectedAmount.add( expectedAmount = expectedAmount.add(

View File

@ -43,9 +43,11 @@
<PaymentModal <PaymentModal
:open-modal="openPaymentModal" :open-modal="openPaymentModal"
@toggle-modal="emitEvent('toggleModal', 'Payment')" @toggle-modal="emitEvent('toggleModal', 'Payment')"
@set-cash-amount="(amount) => emitEvent('setCashAmount', amount)" @set-paid-amount="(amount) => emitEvent('setPaidAmount', amount)"
@set-payment-method="
(paymentMethod) => emitEvent('setPaymentMethod', paymentMethod)
"
@set-transfer-ref-no="(ref) => emitEvent('setTransferRefNo', ref)" @set-transfer-ref-no="(ref) => emitEvent('setTransferRefNo', ref)"
@set-transfer-amount="(amount) => emitEvent('setTransferAmount', amount)"
@set-transfer-clearance-date=" @set-transfer-clearance-date="
(date) => emitEvent('setTransferClearanceDate', date) (date) => emitEvent('setTransferClearanceDate', date)
" "
@ -349,7 +351,7 @@ export default defineComponent({
ModernPOSSelectedItemTable, ModernPOSSelectedItemTable,
}, },
props: { props: {
cashAmount: Money, paidAmount: Money,
tableView: Boolean, tableView: Boolean,
itemDiscounts: Money, itemDiscounts: Money,
openAlertModal: Boolean, openAlertModal: Boolean,
@ -402,10 +404,11 @@ export default defineComponent({
'toggleModal', 'toggleModal',
'setCustomer', 'setCustomer',
'clearValues', 'clearValues',
'setCashAmount', 'setPaidAmount',
'setCouponsCount', 'setCouponsCount',
'routeToSinvList', 'routeToSinvList',
'setLoyaltyPoints', 'setLoyaltyPoints',
'setPaymentMethod',
'setTransferRefNo', 'setTransferRefNo',
'applyPricingRule', 'applyPricingRule',
'saveInvoiceAction', 'saveInvoiceAction',
@ -425,7 +428,10 @@ export default defineComponent({
}; };
}, },
methods: { methods: {
emitEvent(eventName: PosEmits, ...args: (string | boolean | Item)[]) { emitEvent(
eventName: PosEmits,
...args: (string | boolean | Item | Money)[]
) {
this.$emit(eventName, ...args); this.$emit(eventName, ...args);
}, },
selectedRow(row: SalesInvoiceItem, field: string) { selectedRow(row: SalesInvoiceItem, field: string) {

View File

@ -141,16 +141,13 @@ export default defineComponent({
this.posShiftDoc.openingAmounts = []; this.posShiftDoc.openingAmounts = [];
await this.posShiftDoc.set('openingAmounts', [ const paymentMethods = (
{ (await this.fyo.db.getAll(ModelNameEnum.PaymentMethod, {
paymentMethod: 'Cash', fields: ['name'],
amount: fyo.pesa(0), })) as { name: string }[]
}, ).map((doc) => ({ paymentMethod: doc.name, amount: fyo.pesa(0) }));
{
paymentMethod: 'Transfer', await this.posShiftDoc.set('openingAmounts', paymentMethods);
amount: fyo.pesa(0),
},
]);
}, },
async seedDefaults() { async seedDefaults() {
if (!!this.posShiftDoc?.isShiftOpen) { if (!!this.posShiftDoc?.isShiftOpen) {

View File

@ -20,7 +20,6 @@
:default-customer="defaultCustomer" :default-customer="defaultCustomer"
:is-pos-shift-open="isPosShiftOpen" :is-pos-shift-open="isPosShiftOpen"
:items="(items as [] as POSItem[])" :items="(items as [] as POSItem[])"
:cash-amount="(cashAmount as Money)"
:sinv-doc="(sinvDoc as SalesInvoice)" :sinv-doc="(sinvDoc as SalesInvoice)"
:disable-pay-button="disablePayButton" :disable-pay-button="disablePayButton"
:open-payment-modal="openPaymentModal" :open-payment-modal="openPaymentModal"
@ -39,7 +38,8 @@
@clear-values="clearValues" @clear-values="clearValues"
@set-customer="setCustomer" @set-customer="setCustomer"
@toggle-modal="toggleModal" @toggle-modal="toggleModal"
@set-cash-amount="setCashAmount" @set-paid-amount="setPaidAmount"
@set-payment-method="setPaymentMethod"
@set-coupons-count="setCouponsCount" @set-coupons-count="setCouponsCount"
@route-to-sinv-list="routeToSinvList" @route-to-sinv-list="routeToSinvList"
@set-loyalty-points="setLoyaltyPoints" @set-loyalty-points="setLoyaltyPoints"
@ -61,7 +61,6 @@
:default-customer="defaultCustomer" :default-customer="defaultCustomer"
:is-pos-shift-open="isPosShiftOpen" :is-pos-shift-open="isPosShiftOpen"
:items="(items as [] as POSItem[])" :items="(items as [] as POSItem[])"
:cash-amount="(cashAmount as Money)"
:sinv-doc="(sinvDoc as SalesInvoice)" :sinv-doc="(sinvDoc as SalesInvoice)"
:disable-pay-button="disablePayButton" :disable-pay-button="disablePayButton"
:open-payment-modal="openPaymentModal" :open-payment-modal="openPaymentModal"
@ -81,7 +80,8 @@
@clear-values="clearValues" @clear-values="clearValues"
@set-customer="setCustomer" @set-customer="setCustomer"
@toggle-modal="toggleModal" @toggle-modal="toggleModal"
@set-cash-amount="setCashAmount" @set-paid-amount="setPaidAmount"
@set-payment-method="setPaymentMethod"
@set-coupons-count="setCouponsCount" @set-coupons-count="setCouponsCount"
@route-to-sinv-list="routeToSinvList" @route-to-sinv-list="routeToSinvList"
@apply-pricing-rule="applyPricingRule" @apply-pricing-rule="applyPricingRule"
@ -155,7 +155,8 @@ export default defineComponent({
sinvDoc: computed(() => this.sinvDoc), sinvDoc: computed(() => this.sinvDoc),
coupons: computed(() => this.coupons), coupons: computed(() => this.coupons),
itemQtyMap: computed(() => this.itemQtyMap), itemQtyMap: computed(() => this.itemQtyMap),
cashAmount: computed(() => this.cashAmount), paidAmount: computed(() => this.paidAmount),
paymentMethod: computed(() => this.paymentMethod),
transferRefNo: computed(() => this.transferRefNo), transferRefNo: computed(() => this.transferRefNo),
itemDiscounts: computed(() => this.itemDiscounts), itemDiscounts: computed(() => this.itemDiscounts),
transferAmount: computed(() => this.transferAmount), transferAmount: computed(() => this.transferAmount),
@ -188,7 +189,7 @@ export default defineComponent({
openAppliedCouponsModal: false, openAppliedCouponsModal: false,
totalQuantity: 0, totalQuantity: 0,
cashAmount: fyo.pesa(0), paidAmount: fyo.pesa(0),
itemDiscounts: fyo.pesa(0), itemDiscounts: fyo.pesa(0),
transferAmount: fyo.pesa(0), transferAmount: fyo.pesa(0),
totalTaxedAmount: fyo.pesa(0), totalTaxedAmount: fyo.pesa(0),
@ -202,6 +203,7 @@ export default defineComponent({
appliedCoupons: [] as AppliedCouponCodes[], appliedCoupons: [] as AppliedCouponCodes[],
itemSearchTerm: '', itemSearchTerm: '',
paymentMethod: undefined as string | undefined,
transferRefNo: undefined as string | undefined, transferRefNo: undefined as string | undefined,
defaultCustomer: undefined as string | undefined, defaultCustomer: undefined as string | undefined,
transferClearanceDate: undefined as Date | undefined, transferClearanceDate: undefined as Date | undefined,
@ -396,8 +398,11 @@ export default defineComponent({
toggleView() { toggleView() {
this.tableView = !this.tableView; this.tableView = !this.tableView;
}, },
setCashAmount(amount: Money) { setPaidAmount(amount: Money) {
this.cashAmount = amount; this.paidAmount = amount;
},
setPaymentMethod(method: string) {
this.paymentMethod = method;
}, },
setDefaultCustomer() { setDefaultCustomer() {
this.defaultCustomer = this.fyo.singles.Defaults?.posCustomer ?? ''; this.defaultCustomer = this.fyo.singles.Defaults?.posCustomer ?? '';
@ -579,22 +584,26 @@ export default defineComponent({
}, },
async makePayment() { async makePayment() {
this.paymentDoc = this.sinvDoc.getPayment() as Payment; this.paymentDoc = this.sinvDoc.getPayment() as Payment;
const paymentMethod = this.cashAmount.isZero() ? 'Transfer' : 'Cash'; const paymentMethod = this.paymentMethod;
await this.paymentDoc.set('paymentMethod', paymentMethod); await this.paymentDoc.set('paymentMethod', paymentMethod);
if (paymentMethod === 'Transfer') { const paymentMethodDoc = await this.paymentDoc.loadAndGetLink(
'paymentMethod'
);
if (paymentMethodDoc?.type !== 'Cash') {
await this.paymentDoc.setMultiple({ await this.paymentDoc.setMultiple({
amount: this.transferAmount as Money, amount: this.paidAmount as Money,
referenceId: this.transferRefNo, referenceId: this.transferRefNo,
clearanceDate: this.transferClearanceDate, clearanceDate: this.transferClearanceDate,
}); });
} }
if (paymentMethod === 'Cash') { if (paymentMethodDoc?.type === 'Cash') {
await this.paymentDoc.setMultiple({ await this.paymentDoc.setMultiple({
paymentAccount: this.defaultPOSCashAccount, paymentAccount: this.defaultPOSCashAccount,
amount: this.cashAmount as Money, amount: this.paidAmount as Money,
}); });
} }
@ -688,7 +697,7 @@ export default defineComponent({
this.setSinvDoc(); this.setSinvDoc();
this.itemSerialNumbers = {}; this.itemSerialNumbers = {};
this.cashAmount = fyo.pesa(0); this.paidAmount = fyo.pesa(0);
this.transferAmount = fyo.pesa(0); this.transferAmount = fyo.pesa(0);
await this.setItems(); await this.setItems();

View File

@ -1,43 +1,24 @@
<template> <template>
<Modal class="w-2/6 ml-auto mr-3.5" :set-close-listener="false"> <Modal class="w-2/6 ml-auto mr-3.5" :set-close-listener="false">
<div v-if="sinvDoc.fieldMap" class="px-4 py-6 grid" style="height: 95vh"> <div v-if="sinvDoc.fieldMap" class="px-4 py-6 grid" style="height: 95vh">
<Currency
:df="fyo.fieldMap.PaymentFor.amount"
:read-only="!transferAmount.isZero()"
:border="true"
:text-right="true"
:value="paidAmount"
@change="(amount:Money)=> $emit('setPaidAmount', amount)"
/>
<div class="grid grid-cols-2 gap-6"> <div class="grid grid-cols-2 gap-6">
<Currency
:df="fyo.fieldMap.PaymentFor.amount"
:read-only="!transferAmount.isZero()"
:border="true"
:text-right="true"
:value="cashAmount"
@change="(amount:Money)=> $emit('setCashAmount', amount)"
/>
<Button <Button
v-for="method in paymentMethods"
:key="method"
class="w-full py-5 bg-teal-500" class="w-full py-5 bg-teal-500"
@click="setCashOrTransferAmount" @click="setPaymentMethodAndAmount(method, paidAmount)"
> >
<slot> <slot>
<p class="uppercase text-lg text-white font-semibold"> <p class="uppercase text-lg text-white font-semibold">
{{ t`Cash` }} {{ t`${method}` }}
</p>
</slot>
</Button>
<Currency
:df="fyo.fieldMap.PaymentFor.amount"
:read-only="!cashAmount.isZero()"
:border="true"
:text-right="true"
:value="transferAmount"
@change="(value:Money)=> $emit('setTransferAmount', value)"
/>
<Button
class="w-full py-5 bg-teal-500"
@click="setCashOrTransferAmount('Transfer')"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Transfer` }}
</p> </p>
</slot> </slot>
</Button> </Button>
@ -45,7 +26,7 @@
<div class="mt-8 grid grid-cols-2 gap-6"> <div class="mt-8 grid grid-cols-2 gap-6">
<Data <Data
v-show="!transferAmount.isZero()" v-show="!isPaymentMethodIsCash"
:df="fyo.fieldMap.Payment.referenceId" :df="fyo.fieldMap.Payment.referenceId"
:show-label="true" :show-label="true"
:border="true" :border="true"
@ -55,7 +36,7 @@
/> />
<Date <Date
v-show="!transferAmount.isZero()" v-show="!isPaymentMethodIsCash"
:df="fyo.fieldMap.Payment.clearanceDate" :df="fyo.fieldMap.Payment.clearanceDate"
:show-label="true" :show-label="true"
:border="true" :border="true"
@ -149,7 +130,7 @@
/> />
</div> </div>
<div class="row-start-6 grid grid-cols-2 gap-4 mt-auto"> <div class="grid grid-cols-2 gap-4 fixed bottom-8" style="width: 25rem">
<div class="col-span-2"> <div class="col-span-2">
<Button <Button
class="w-full bg-red-500 dark:bg-red-700" class="w-full bg-red-500 dark:bg-red-700"
@ -207,6 +188,8 @@ import { Money } from 'pesa';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice'; import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { defineComponent, inject } from 'vue'; import { defineComponent, inject } from 'vue';
import { fyo } from 'src/initFyo'; import { fyo } from 'src/initFyo';
import { isPesa } from 'fyo/utils';
import { ModelNameEnum } from 'models/types';
export default defineComponent({ export default defineComponent({
name: 'PaymentModal', name: 'PaymentModal',
@ -219,15 +202,16 @@ export default defineComponent({
}, },
emits: [ emits: [
'createTransaction', 'createTransaction',
'setCashAmount', 'setPaidAmount',
'setTransferAmount', 'setPaymentMethod',
'setTransferClearanceDate', 'setTransferClearanceDate',
'setTransferRefNo', 'setTransferRefNo',
'toggleModal', 'toggleModal',
], ],
setup() { setup() {
return { return {
cashAmount: inject('cashAmount') as Money, paidAmount: inject('paidAmount') as Money,
paymentMethod: inject('paymentMethod') as string,
isDiscountingEnabled: inject('isDiscountingEnabled') as boolean, isDiscountingEnabled: inject('isDiscountingEnabled') as boolean,
itemDiscounts: inject('itemDiscounts') as Money, itemDiscounts: inject('itemDiscounts') as Money,
transferAmount: inject('transferAmount') as Money, transferAmount: inject('transferAmount') as Money,
@ -237,34 +221,46 @@ export default defineComponent({
totalTaxedAmount: inject('totalTaxedAmount') as Money, totalTaxedAmount: inject('totalTaxedAmount') as Money,
}; };
}, },
data() {
return {
paymentMethods: [] as string[],
};
},
computed: { computed: {
isPaymentMethodIsCash(): boolean {
return this.paymentMethod === 'Cash';
},
balanceAmount(): Money { balanceAmount(): Money {
const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0); const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0);
if (this.cashAmount.isZero()) { if (isPesa(this.paidAmount) && this.paidAmount.isZero()) {
return grandTotal.sub(this.transferAmount); return grandTotal.sub(this.transferAmount);
} }
return grandTotal.sub(this.cashAmount); return grandTotal.sub(this.paidAmount);
}, },
paidChange(): Money { paidChange(): Money {
const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0); const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0);
if (this.cashAmount.isZero()) { if (this.fyo.pesa(this.paidAmount.float).isZero()) {
return this.transferAmount.sub(grandTotal); return this.transferAmount.sub(grandTotal);
} }
return this.cashAmount.sub(grandTotal); return this.fyo.pesa(this.paidAmount.float).sub(grandTotal);
}, },
showBalanceAmount(): boolean { showBalanceAmount(): boolean {
if ( if (
this.cashAmount.eq(fyo.pesa(0)) && this.fyo.pesa(this.paidAmount.float).eq(fyo.pesa(0)) &&
this.transferAmount.eq(fyo.pesa(0)) this.transferAmount.eq(fyo.pesa(0))
) { ) {
return false; return false;
} }
if (this.cashAmount.gte(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) { if (
this.fyo
.pesa(this.paidAmount.float)
.gte(this.sinvDoc?.grandTotal ?? fyo.pesa(0))
) {
return false; return false;
} }
@ -276,13 +272,17 @@ export default defineComponent({
}, },
showPaidChange(): boolean { showPaidChange(): boolean {
if ( if (
this.cashAmount.eq(fyo.pesa(0)) && this.fyo.pesa(this.paidAmount.float).eq(fyo.pesa(0)) &&
this.transferAmount.eq(fyo.pesa(0)) this.transferAmount.eq(fyo.pesa(0))
) { ) {
return false; return false;
} }
if (this.cashAmount.gt(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) { if (
this.fyo
.pesa(this.paidAmount.float)
.gt(this.sinvDoc?.grandTotal ?? fyo.pesa(0))
) {
return true; return true;
} }
@ -294,15 +294,14 @@ export default defineComponent({
}, },
disableSubmitButton(): boolean { disableSubmitButton(): boolean {
if ( if (
!this.sinvDoc.grandTotal?.isZero() && (this.sinvDoc.grandTotal?.float as number) < 1 &&
this.transferAmount.isZero() && this.fyo.pesa(this.paidAmount.float).isZero()
this.cashAmount.isZero()
) { ) {
return true; return true;
} }
if ( if (
this.cashAmount.isZero() && this.paymentMethod !== 'Cash' &&
(!this.transferRefNo || !this.transferClearanceDate) (!this.transferRefNo || !this.transferClearanceDate)
) { ) {
return true; return true;
@ -310,15 +309,25 @@ export default defineComponent({
return false; return false;
}, },
}, },
async activated() {
await this.setPaymentMethods();
},
methods: { methods: {
setCashOrTransferAmount(paymentMethod = 'Cash') { setPaymentMethodAndAmount(paymentMethod?: string, amount?: Money) {
if (paymentMethod === 'Transfer') { if (paymentMethod) {
this.$emit('setCashAmount', fyo.pesa(0)); this.$emit('setPaymentMethod', paymentMethod);
this.$emit('setTransferAmount', this.sinvDoc?.grandTotal);
return;
} }
this.$emit('setTransferAmount', fyo.pesa(0));
this.$emit('setCashAmount', this.sinvDoc?.grandTotal); if (amount) {
this.$emit('setPaidAmount', this.sinvDoc.grandTotal?.float);
}
},
async setPaymentMethods() {
this.paymentMethods = (
(await this.fyo.db.getAll(ModelNameEnum.PaymentMethod, {
fields: ['name'],
})) as { name: string }[]
).map((d) => d.name);
}, },
submitTransaction() { submitTransaction() {
this.$emit('createTransaction'); this.$emit('createTransaction');