diff --git a/frappe/model/document.js b/frappe/model/document.js index c5ac3107..e635f18c 100644 --- a/frappe/model/document.js +++ b/frappe/model/document.js @@ -710,6 +710,41 @@ module.exports = class BaseDocument extends Observable { return obj; }, {}); } + + async duplicate() { + const updateMap = {}; + const fieldValueMap = this.getValidDict(); + const keys = this.meta.fields.map((f) => f.fieldname); + for (const key of keys) { + let value = fieldValueMap[key]; + if (!value) { + continue; + } + + if (isPesa(value)) { + value = value.copy(); + } + + if (value instanceof Array) { + value.forEach((row) => { + delete row.name; + delete row.parent; + }); + } + + updateMap[key] = value; + } + + if (this.numberSeries) { + delete updateMap.name; + } else { + updateMap.name = updateMap.name + ' CPY'; + } + + const doc = frappe.getNewDoc(this.doctype, false); + await doc.set(updateMap); + await doc.insert(); + } }; function getPreDefaultValues(fieldtype) { diff --git a/src/utils.js b/src/utils.js index 638fec3c..87b8a803 100644 --- a/src/utils.js +++ b/src/utils.js @@ -227,7 +227,40 @@ export function getActionsForDocument(doc) { }, }; - let actions = [...(doc.meta.actions || []), deleteAction, cancelAction] + const isSubmittable = !!doc.meta.isSubmittable; + const duplicateAction = { + label: frappe.t`Duplicate`, + condition: (doc) => + ((isSubmittable && doc && doc.submitted) || !isSubmittable) && + !doc._notInserted && + !(doc.cancelled || false), + action: () => { + showMessageDialog({ + message: t`Duplicate ${doc.doctype} ${doc.name}?`, + buttons: [ + { + label: t`Yes`, + async action() { + doc.duplicate(); + }, + }, + { + label: t`No`, + action() { + resolve(false); + }, + }, + ], + }); + }, + }; + + let actions = [ + ...(doc.meta.actions || []), + duplicateAction, + deleteAction, + cancelAction, + ] .filter((d) => (d.condition ? d.condition(doc) : true)) .map((d) => { return {