mirror of
https://github.com/frappe/books.git
synced 2024-12-24 03:45:35 +00:00
Merge pull request #256 from 18alantom/fix-oustanding-reversion
fix: multiple payment related fixes
This commit is contained in:
commit
24e43d1ea0
@ -38,6 +38,11 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
formula: (doc) => {
|
||||
if (doc.paymentMethod === 'Cash' && doc.paymentType === 'Pay') {
|
||||
return 'Cash';
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: 'paymentType',
|
||||
@ -63,7 +68,7 @@ export default {
|
||||
}
|
||||
},
|
||||
formula: (doc) => {
|
||||
if (doc.paymentMethod === 'Cash') {
|
||||
if (doc.paymentMethod === 'Cash' && doc.paymentType === 'Receive') {
|
||||
return 'Cash';
|
||||
}
|
||||
},
|
||||
@ -107,7 +112,7 @@ export default {
|
||||
if (value < 0) {
|
||||
throw new frappe.errors.ValidationError(
|
||||
frappe._(
|
||||
`Payment amount cannot be less than zero. Amount has been reset to max viable amount.`
|
||||
`Payment amount cannot be less than zero. Amount has been reset.`
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -121,7 +126,7 @@ export default {
|
||||
`Payment amount cannot exceed ${frappe.format(
|
||||
amount,
|
||||
'Currency'
|
||||
)}. Amount has been reset to max viable amount.`
|
||||
)}. Amount has been reset.`
|
||||
)
|
||||
);
|
||||
} else if (value === 0) {
|
||||
@ -130,7 +135,7 @@ export default {
|
||||
`Payment amount cannot be ${frappe.format(
|
||||
value,
|
||||
'Currency'
|
||||
)}. Amount has been reset to max viable amount.`
|
||||
)}. Amount has been reset.`
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -140,6 +145,7 @@ export default {
|
||||
fieldname: 'writeoff',
|
||||
label: 'Write Off / Refund',
|
||||
fieldtype: 'Currency',
|
||||
default: 0,
|
||||
},
|
||||
{
|
||||
fieldname: 'for',
|
||||
|
@ -1,14 +1,91 @@
|
||||
import BaseDocument from 'frappejs/model/document';
|
||||
import frappe from 'frappejs';
|
||||
import BaseDocument from 'frappejs/model/document';
|
||||
import LedgerPosting from '../../../accounting/ledgerPosting';
|
||||
|
||||
export default class PaymentServer extends BaseDocument {
|
||||
async change({ changed }) {
|
||||
if (changed === 'for') {
|
||||
this.amount = 0;
|
||||
for (let paymentReference of this.for) {
|
||||
this.amount += paymentReference.amount;
|
||||
switch (changed) {
|
||||
case 'for': {
|
||||
this.updateAmountOnReferenceUpdate();
|
||||
await this.updateDetailsOnReferenceUpdate();
|
||||
}
|
||||
case 'amount': {
|
||||
this.updateReferenceOnAmountUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateDetailsOnReferenceUpdate() {
|
||||
const { referenceType, referenceName } = this.for[0];
|
||||
if (
|
||||
this.for?.length !== 1 ||
|
||||
this.party ||
|
||||
this.paymentType ||
|
||||
!referenceName ||
|
||||
!referenceType
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doctype = referenceType;
|
||||
const doc = await frappe.getDoc(doctype, referenceName);
|
||||
|
||||
let party;
|
||||
let paymentType;
|
||||
|
||||
if (doctype === 'SalesInvoice') {
|
||||
party = doc.customer;
|
||||
paymentType = 'Receive';
|
||||
} else if (doctype === 'PurchaseInvoice') {
|
||||
party = doc.supplier;
|
||||
paymentType = 'Pay';
|
||||
}
|
||||
|
||||
this.party = party;
|
||||
this.paymentType = paymentType;
|
||||
}
|
||||
|
||||
updateAmountOnReferenceUpdate() {
|
||||
this.amount = 0;
|
||||
for (let paymentReference of this.for) {
|
||||
this.amount += paymentReference.amount;
|
||||
}
|
||||
}
|
||||
|
||||
updateReferenceOnAmountUpdate() {
|
||||
if (this.for?.length !== 1) return;
|
||||
this.for[0].amount = this.amount;
|
||||
}
|
||||
|
||||
async validate() {
|
||||
this.validateAccounts();
|
||||
this.validateReferenceAmount();
|
||||
}
|
||||
|
||||
validateAccounts() {
|
||||
if (this.paymentAccount !== this.account || !this.account) return;
|
||||
throw new Error(
|
||||
`To Account and From Account can't be the same: ${this.account}`
|
||||
);
|
||||
}
|
||||
|
||||
validateReferenceAmount() {
|
||||
const referenceAmountTotal = this.for
|
||||
.map(({ amount }) => amount)
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
if (this.amount + (this.writeoff ?? 0) < referenceAmountTotal) {
|
||||
const writeoff = frappe.format(this.writeoff, 'Currency');
|
||||
const payment = frappe.format(this.amount, 'Currency');
|
||||
const refAmount = frappe.format(referenceAmountTotal, 'Currency');
|
||||
const writeoffString =
|
||||
this.writeoff > 0 ? `and writeoff: ${writeoff} ` : '';
|
||||
|
||||
throw new Error(
|
||||
frappe._(
|
||||
`Amount: ${payment} ${writeoffString}is less than the total amount allocated to references: ${refAmount}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,15 +132,16 @@ export default class PaymentServer extends BaseDocument {
|
||||
}
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.post();
|
||||
}
|
||||
|
||||
async afterRevert() {
|
||||
this.updateReferenceOutstandingAmount();
|
||||
const entries = await this.getPosting();
|
||||
await entries.postReverse();
|
||||
}
|
||||
|
||||
// Maybe revert outstanding amount of invoice too?
|
||||
async updateReferenceOutstandingAmount() {
|
||||
await this.for.forEach(async ({ amount, referenceType, referenceName }) => {
|
||||
const refDoc = await frappe.getDoc(referenceType, referenceName);
|
||||
refDoc.update({ outstandingAmount: refDoc.outstandingAmount + amount });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,34 +4,40 @@ export default {
|
||||
isSingle: 0,
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
tableFields: [
|
||||
'referenceType',
|
||||
'referenceName',
|
||||
'amount'
|
||||
],
|
||||
tableFields: ['referenceType', 'referenceName', 'amount'],
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'referenceType',
|
||||
label: 'Reference Type',
|
||||
fieldtype: 'AutoComplete',
|
||||
fieldtype: 'Select',
|
||||
options: ['SalesInvoice', 'PurchaseInvoice'],
|
||||
required: 1
|
||||
map: { SalesInvoice: 'Invoice', PurchaseInvoice: 'Bill' },
|
||||
required: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'referenceName',
|
||||
label: 'Reference Name',
|
||||
fieldtype: 'DynamicLink',
|
||||
references: 'referenceType',
|
||||
required: 1
|
||||
getFilters() {
|
||||
return {
|
||||
outstandingAmount: ['>', 0],
|
||||
};
|
||||
},
|
||||
required: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'amount',
|
||||
label: 'Amount',
|
||||
fieldtype: 'Currency',
|
||||
formula: (row, doc) => {
|
||||
return doc.getFrom(row.referenceType, row.referenceName, 'outstandingAmount');
|
||||
return doc.getFrom(
|
||||
row.referenceType,
|
||||
row.referenceName,
|
||||
'outstandingAmount'
|
||||
);
|
||||
},
|
||||
required: 1
|
||||
}
|
||||
]
|
||||
required: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -4,15 +4,22 @@
|
||||
{{ df.label }}
|
||||
</div>
|
||||
<div
|
||||
class="relative flex items-center justify-end bg-white focus-within:bg-gray-200"
|
||||
class="
|
||||
relative
|
||||
flex
|
||||
items-center
|
||||
justify-end
|
||||
bg-white
|
||||
focus-within:bg-gray-200
|
||||
"
|
||||
:class="inputClasses"
|
||||
>
|
||||
<select
|
||||
class="appearance-none bg-transparent focus:outline-none w-full"
|
||||
:class="isReadOnly && 'pointer-events-none'"
|
||||
:value="value"
|
||||
@change="e => triggerChange(e.target.value)"
|
||||
@focus="e => $emit('focus', e)"
|
||||
@change="(e) => triggerChange(e.target.value)"
|
||||
@focus="(e) => $emit('focus', e)"
|
||||
>
|
||||
<option
|
||||
v-for="option in options"
|
||||
@ -52,16 +59,24 @@ import Base from './Base';
|
||||
export default {
|
||||
name: 'Select',
|
||||
extends: Base,
|
||||
methods: {
|
||||
map(v) {
|
||||
if (this.df.map) {
|
||||
return this.df.map[v] ?? v;
|
||||
}
|
||||
return v;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
options() {
|
||||
let options = this.df.options;
|
||||
return options.map(o => {
|
||||
return options.map((o) => {
|
||||
if (typeof o === 'string') {
|
||||
return { label: o, value: o };
|
||||
return { label: this.map(o), value: o };
|
||||
}
|
||||
return o;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user