2
0
mirror of https://github.com/frappe/books.git synced 2024-11-14 01:14:03 +00:00

Add ability to cancel invoice

This commit is contained in:
Piyush Singhania 2021-11-21 19:08:04 +05:30
parent aa6a565f9b
commit 1ffd043ca9
12 changed files with 116 additions and 33 deletions

View File

@ -9,7 +9,8 @@ module.exports = {
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"arrow-body-style": "off", "arrow-body-style": "off",
"prefer-arrow-callback": "off", "prefer-arrow-callback": "off",
"vue/multi-word-component-names": "off" "vue/multi-word-component-names": "off",
"vue/no-useless-template-attributes": "off",
}, },
parserOptions: { parserOptions: {
parser: "@babel/eslint-parser" parser: "@babel/eslint-parser"

View File

@ -57,15 +57,12 @@ export default {
fieldtype: 'Text', fieldtype: 'Text',
placeholder: 'User Remark', placeholder: 'User Remark',
}, },
],
actions: [
{ {
label: 'Revert', fieldname: 'cancelled',
condition: (doc) => doc.submitted, label: 'Cancelled',
action(doc) { fieldtype: 'Check',
doc.revert(); default: 0,
},
}, },
ledgerLink,
], ],
actions: [ledgerLink],
}; };

View File

@ -1,4 +1,5 @@
import { _ } from 'frappejs/utils'; import { _ } from 'frappejs/utils';
import Badge from '@/components/Badge';
export default { export default {
doctype: 'JournalEntry', doctype: 'JournalEntry',
@ -6,6 +7,29 @@ export default {
formRoute: name => `/edit/JournalEntry/${name}`, formRoute: name => `/edit/JournalEntry/${name}`,
columns: [ columns: [
'date', 'date',
{
label: 'Status',
fieldtype: 'Select',
size: 'small',
render(doc) {
let status = 'Draft';
let color = 'gray';
if (doc.submitted === 1) {
color = 'green';
status = 'Submitted';
}
if (doc.cancelled === 1) {
color = 'red';
status = 'Cancelled';
}
return {
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,
components: { Badge },
};
},
},
{ {
label: 'Entry ID', label: 'Entry ID',
fieldname: 'name', fieldname: 'name',

View File

@ -140,6 +140,12 @@ export default {
childtype: 'PaymentFor', childtype: 'PaymentFor',
required: 0, required: 0,
}, },
{
fieldname: 'cancelled',
label: 'Cancelled',
fieldtype: 'Check',
default: 0,
},
], ],
quickEditFields: [ quickEditFields: [
@ -206,16 +212,6 @@ export default {
], ],
}, },
], ],
actions: [ actions: [utils.ledgerLink],
{
label: 'Revert',
condition: (doc) => doc.submitted,
action(doc) {
doc.revert();
},
},
utils.ledgerLink,
],
links: [utils.ledgerLink], links: [utils.ledgerLink],
}; };

View File

@ -21,6 +21,10 @@ export default {
color = 'green'; color = 'green';
status = 'Submitted'; status = 'Submitted';
} }
if (doc.cancelled === 1) {
color = 'red';
status = 'Cancelled';
}
return { return {
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`, template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,

View File

@ -123,6 +123,12 @@ export default {
fieldname: 'terms', fieldname: 'terms',
label: 'Terms', label: 'Terms',
fieldtype: 'Text' fieldtype: 'Text'
},
{
fieldname: 'cancelled',
label: 'Cancelled',
fieldtype: 'Check',
default: 0
} }
], ],

View File

@ -123,6 +123,12 @@ export default {
label: 'Notes', label: 'Notes',
fieldtype: 'Text', fieldtype: 'Text',
}, },
{
fieldname: 'cancelled',
label: 'Cancelled',
fieldtype: 'Check',
default: 0,
},
], ],
actions: getActions('SalesInvoice'), actions: getActions('SalesInvoice'),

View File

@ -53,14 +53,6 @@ export function getActions(doctype) {
}); });
}, },
}, },
{
label: 'Revert',
condition: (doc) =>
doc.submitted && doc.baseGrandTotal === doc.outstandingAmount,
action(doc) {
doc.revert();
},
},
{ {
label: 'Print', label: 'Print',
condition: (doc) => doc.submitted, condition: (doc) => doc.submitted,

View File

@ -49,7 +49,11 @@ export default {
const paymentEntries = await payment.getPosting(); const paymentEntries = await payment.getPosting();
await paymentEntries.postReverse(); await paymentEntries.postReverse();
// To set the payment status as unsubmitted. // To set the payment status as unsubmitted.
payment.revert(); await frappe.db.update('Payment', {
name: paymentReference,
submitted: 0,
cancelled: 1,
});
} }
const entries = await this.getPosting(); const entries = await this.getPosting();
await entries.postReverse(); await entries.postReverse();

View File

@ -12,6 +12,7 @@ class Cashflow {
let dateAsMonthYear = frappe.db.knex.raw('strftime("%m-%Y", ??)', 'date'); let dateAsMonthYear = frappe.db.knex.raw('strftime("%m-%Y", ??)', 'date');
let res = await frappe.db let res = await frappe.db
.knex('AccountingLedgerEntry') .knex('AccountingLedgerEntry')
.where('reverted', 0)
.sum({ .sum({
inflow: 'debit', inflow: 'debit',
outflow: 'credit' outflow: 'credit'

View File

@ -60,8 +60,8 @@ const viewConfig = {
{ {
fieldtype: 'Select', fieldtype: 'Select',
options: [ options: [
{ label: 'Show Reverted', value: '' }, { label: 'Show Cancelled', value: '' },
{ label: 'Hide Reverted', value: '0' } { label: 'Hide Cancelled', value: '0' }
], ],
size: 'small', size: 'small',
default: '0', default: '0',

View File

@ -145,6 +145,40 @@ export function deleteDocWithPrompt(doc) {
}); });
} }
export function cancelDocWithPrompt(doc) {
return new Promise((resolve) => {
showMessageDialog({
message: _('Are you sure you want to cancel {0} "{1}"?', [
doc.doctype,
doc.name,
]),
description: _('This action is permanent'),
buttons: [
{
label: _('Yes'),
async action() {
const entryDoc = await frappe.getDoc(doc.doctype, doc.name);
entryDoc.cancelled = 1;
await entryDoc.update();
entryDoc
.revert()
.then(() => resolve(true))
.catch((e) => {
handleErrorWithDialog(e, doc);
});
},
},
{
label: _('No'),
action() {
resolve(false);
},
},
],
});
});
}
export function partyWithAvatar(party) { export function partyWithAvatar(party) {
return { return {
data() { data() {
@ -222,7 +256,7 @@ export function getActionsForDocument(doc) {
component: { component: {
template: `<span class="text-red-700">{{ _('Delete') }}</span>`, template: `<span class="text-red-700">{{ _('Delete') }}</span>`,
}, },
condition: (doc) => !doc.isNew() && !doc.submitted && !doc.meta.isSingle, condition: (doc) => !doc.isNew() && !doc.submitted && !doc.meta.isSingle && !doc.cancelled,
action: () => action: () =>
deleteDocWithPrompt(doc).then((res) => { deleteDocWithPrompt(doc).then((res) => {
if (res) { if (res) {
@ -231,7 +265,21 @@ export function getActionsForDocument(doc) {
}), }),
}; };
let actions = [...(doc.meta.actions || []), deleteAction] let cancelAction = {
component: {
template: `<span class="text-red-700">{{ _('Cancel') }}</span>`,
},
condition: (doc) => doc.submitted && !doc.cancelled,
action: () => {
cancelDocWithPrompt(doc).then((res) => {
if (res) {
router.push(`/list/${doc.doctype}`);
}
});
},
};
let actions = [...(doc.meta.actions || []), deleteAction, cancelAction]
.filter((d) => (d.condition ? d.condition(doc) : true)) .filter((d) => (d.condition ? d.condition(doc) : true))
.map((d) => { .map((d) => {
return { return {
@ -270,6 +318,7 @@ export const statusColor = {
Draft: 'gray', Draft: 'gray',
Unpaid: 'orange', Unpaid: 'orange',
Paid: 'green', Paid: 'green',
Cancelled: 'red',
}; };
export function getInvoiceStatus(doc) { export function getInvoiceStatus(doc) {
@ -280,5 +329,8 @@ export function getInvoiceStatus(doc) {
if (doc.submitted === 1 && doc.outstandingAmount === 0.0) { if (doc.submitted === 1 && doc.outstandingAmount === 0.0) {
status = 'Paid'; status = 'Paid';
} }
if (doc.cancelled === 1) {
status = 'Cancelled';
}
return status; return status;
} }