diff --git a/package.json b/package.json index bd1cd93..e7e7c5f 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,11 @@ "author": "Frappe Technologies Pvt. Ltd.", "license": "AGPL-3.0-or-later", "dependencies": { - "@interactjs/interact": "^1.10.17", "@interactjs/actions": "^1.10.17", "@interactjs/auto-start": "^1.10.17", + "@interactjs/interact": "^1.10.17", "@interactjs/modifiers": "^1.10.17", + "html2canvas": "^1.4.1", "pdfjs-dist": "v3.4.120" } } diff --git a/print_designer/custom_fields.py b/print_designer/custom_fields.py index 981b556..c0c1045 100644 --- a/print_designer/custom_fields.py +++ b/print_designer/custom_fields.py @@ -44,5 +44,19 @@ "fieldtype": "JSON", "label": "Print Designer Settings", }, + { + "fieldname": "print_designer_preview_img", + "hidden": 1, + "fieldtype": "Attach Image", + "label": "Print Designer Preview Image", + }, + { + "depends_on": "eval:doc.print_designer && doc.standard == 'Yes'", + "fieldname": "print_designer_template_app", + "fieldtype": "Select", + "label": "Print Designer Template Location", + "default": "print_designer", + "insert_after": "standard", + }, ] } diff --git a/print_designer/default_formats.py b/print_designer/default_formats.py new file mode 100644 index 0000000..2e0e9f9 --- /dev/null +++ b/print_designer/default_formats.py @@ -0,0 +1,117 @@ +import os +import shutil +from pathlib import Path + +import frappe +from frappe.modules.import_file import import_file_by_path +from frappe.utils import get_files_path + +""" +features: + - Print Designer App can have default formats for all installed apps. + - Any Custom/Standard App can have default formats for any installed apps + ( This will only install formats if print_designer is installed ). + - This will be useful when we have standalone formats that can be used without print designer app. + +when print_designer app is installed + - get hooks from all installed apps including pd and load default formats from defined folders. + +when any new app is installed + - if exists in print_designer/default_templates, load default formats for newly installed app. + - get hooks from new app and load default formats for all installed apps from app's format dir. +""" + +# TODO: handle override of default formats from different apps or even Custom Formats with same name. + +# add default formats for all installed apps. +def on_print_designer_install(): + for app in frappe.get_installed_apps(): + install_default_formats(app=app, load_pd_formats=False) + + +def get_preview_image_folder_path(print_format): + app = frappe.scrub(frappe.get_doctype_app(print_format.doc_type)) + pd_folder = frappe.get_hooks( + "pd_standard_format_folder", app_name=print_format.print_designer_template_app + ) + if len(pd_folder) == 0: + pd_folder = ["default_templates"] + return os.path.join( + frappe.get_app_path(print_format.print_designer_template_app), os.path.join(pd_folder[0], app) + ) + + +def update_preview_img(file): + print_format = frappe.get_doc(file.attached_to_doctype, file.attached_to_name) + folder = get_preview_image_folder_path(print_format) + file_name = print_format.print_designer_preview_img.split("/")[-1] + org_path = os.path.join(folder, file_name) + target_path = get_files_path(file_name, is_private=1) + shutil.copy2(org_path, target_path) + + +# called after install of any new app. +def install_default_formats(app, filter_by="", load_pd_formats=True): + if load_pd_formats: + # load formats from print_designer app if some new app is installed and have default formats + install_default_formats(app="print_designer", filter_by=app, load_pd_formats=False) + + # get dir path and load formats from installed app + pd_folder = frappe.get_hooks("pd_standard_format_folder", app_name=app) + if len(pd_folder) == 0: + return + + print_formats = get_filtered_formats_by_app( + app=app, templates_folder=pd_folder[0], filter_by=filter_by + ) + + # preview_files = [f for f in print_formats if f.name.endswith("-preview.json")] + print_formats = [f for f in print_formats if not f.name.endswith("-preview.json")] + + for json_file_path in print_formats: + import_file_by_path(path=json_file_path) + frappe.db.commit() + # TODO: enable this after this is released in v15 https://github.com/frappe/frappe/pull/25779 + # for json_file_path in preview_files: + # import_file_by_path(path=json_file_path, pre_process=update_preview_img) + # frappe.db.commit() + + # for pf in frappe.db.get_all("Print Format", filters={"standard": "Yes", "print_designer": 1}): + # updated_url = frappe.db.get_value( + # "File", + # { + # "attached_to_doctype": "Print Format", + # "attached_to_name": pf.name, + # "attached_to_field": "print_designer_preview_img", + # }, + # "file_url", + # ) + # if updated_url: + # frappe.set_value("Print Format", pf.name, "print_designer_preview_img", updated_url) + + +def get_filtered_formats_by_app(app, templates_folder, filter_by=""): + app_path = frappe.get_app_path(app) + if filter_by == "": + folders = Path(os.path.join(app_path, templates_folder)) + return get_formats_from_folders(folders=folders) + else: + folder = Path(os.path.join(app_path, templates_folder, filter_by)) + return get_json_files(folder) + + +def get_formats_from_folders(folders): + formats = set() + if not folders.exists(): + return formats + for folder in folders.iterdir(): + if folder.is_dir() and folder.name in frappe.get_installed_apps(): + formats.update(get_json_files(folder)) + return formats + + +def get_json_files(folder): + formats = set() + for json_file in folder.glob("*.json"): + formats.add(json_file) + return formats diff --git a/print_designer/hooks.py b/print_designer/hooks.py index 68e9d5e..4b1079b 100644 --- a/print_designer/hooks.py +++ b/print_designer/hooks.py @@ -72,6 +72,7 @@ before_install = "print_designer.install.before_install" after_install = "print_designer.install.after_install" +after_app_install = "print_designer.install.after_app_install" # Uninstallation # ------------ @@ -110,10 +111,12 @@ # --------------- # Override standard doctype classes -# override_doctype_class = { -# "ToDo": "custom_app.overrides.CustomToDo" -# } +override_doctype_class = { + "Print Format": "print_designer.print_designer.overrides.print_format.PDPrintFormat", +} +# Path Relative to the app folder where default templates should be stored +pd_standard_format_folder = "default_templates" # Document Events # --------------- # Hook on document methods and events diff --git a/print_designer/install.py b/print_designer/install.py index af9200c..22ebc80 100644 --- a/print_designer/install.py +++ b/print_designer/install.py @@ -3,6 +3,7 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from print_designer.custom_fields import CUSTOM_FIELDS +from print_designer.default_formats import install_default_formats, on_print_designer_install def check_frappe_version(): @@ -27,3 +28,9 @@ def before_install(): def after_install(): create_custom_fields(CUSTOM_FIELDS, ignore_validate=True) + on_print_designer_install() + + +def after_app_install(app): + if app != "print_designer": + install_default_formats(app) diff --git a/print_designer/patches.txt b/print_designer/patches.txt index 1c006ff..6f8c620 100644 --- a/print_designer/patches.txt +++ b/print_designer/patches.txt @@ -9,3 +9,4 @@ print_designer.patches.introduce_suffix_dynamic_content print_designer.patches.introduce_dynamic_containers print_designer.patches.introduce_dynamic_height print_designer.patches.remove_unused_rectangle_gs_properties +execute:from print_designer.patches.create_custom_fields import custom_field_patch; custom_field_patch() diff --git a/print_designer/patches/create_custom_fields.py b/print_designer/patches/create_custom_fields.py new file mode 100644 index 0000000..304b916 --- /dev/null +++ b/print_designer/patches/create_custom_fields.py @@ -0,0 +1,7 @@ +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +from print_designer.custom_fields import CUSTOM_FIELDS + + +def custom_field_patch(): + create_custom_fields(CUSTOM_FIELDS, ignore_validate=True) diff --git a/print_designer/print_designer/client_scripts/print_format.js b/print_designer/print_designer/client_scripts/print_format.js index 6e88365..c4bacc9 100644 --- a/print_designer/print_designer/client_scripts/print_format.js +++ b/print_designer/print_designer/client_scripts/print_format.js @@ -1,6 +1,16 @@ +const set_template_app_options = (frm) => { + frappe.xcall("frappe.core.doctype.module_def.module_def.get_installed_apps").then((r) => { + frm.set_df_property("print_designer_template_app", "options", JSON.parse(r)); + if (!frm.doc.print_designer_template_app) { + frm.set_value("print_designer_template_app", "print_designer"); + } + }); +}; + frappe.ui.form.on("Print Format", { refresh: function (frm) { frm.trigger("render_buttons"); + set_template_app_options(frm); }, render_buttons: function (frm) { frm.page.clear_inner_toolbar(); diff --git a/print_designer/print_designer/overrides/print_format.py b/print_designer/print_designer/overrides/print_format.py new file mode 100644 index 0000000..6b3ca46 --- /dev/null +++ b/print_designer/print_designer/overrides/print_format.py @@ -0,0 +1,84 @@ +import os +import shutil + +import frappe +from frappe.modules.utils import scrub +from frappe.printing.doctype.print_format.print_format import PrintFormat + + +class PDPrintFormat(PrintFormat): + def export_doc(self): + if ( + not self.standard == "Yes" + or not frappe.conf.developer_mode + or frappe.flags.in_patch + or frappe.flags.in_install + or frappe.flags.in_migrate + or frappe.flags.in_import + or frappe.flags.in_setup_wizard + ): + return + + if not self.print_designer: + return super().export_doc() + + self.write_document_file() + + def write_document_file(self): + doc = self + doc_export = doc.as_dict(no_nulls=True) + doc.run_method("before_export", doc_export) + + # create folder + folder = self.create_folder(doc.doc_type, doc.name) + + fname = scrub(doc.name) + + # write the data file + path = os.path.join(folder, f"{fname}.json") + with open(path, "w+") as json_file: + json_file.write(frappe.as_json(doc_export)) + print(f"Wrote document file for {doc.doctype} {doc.name} at {path}") + self.export_preview(folder=folder, fname=fname) + + def create_folder(self, dt, dn): + app = scrub(frappe.get_doctype_app(dt)) + dn = scrub(dn) + pd_folder = frappe.get_hooks( + "pd_standard_format_folder", app_name=self.print_designer_template_app + ) + if len(pd_folder) == 0: + pd_folder = ["default_templates"] + folder = os.path.join( + frappe.get_app_path(self.print_designer_template_app), os.path.join(pd_folder[0], app) + ) + frappe.create_folder(folder) + return folder + + def export_preview(self, folder, fname): + if self.print_designer_preview_img: + try: + file = frappe.get_doc( + "File", + { + "file_url": self.print_designer_preview_img, + "attached_to_doctype": self.doctype, + "attached_to_name": self.name, + "attached_to_field": "print_designer_preview_img", + }, + ) + except frappe.DoesNotExistError: + file = None + if not file: + return + file_export = file.as_dict(no_nulls=True) + file.run_method("before_export", file_export) + org_path = file.get_full_path() + target_path = os.path.join(folder, org_path.split("/")[-1]) + shutil.copy2(org_path, target_path) + print(f"Wrote preview file for {self.doctype} {self.name} at {target_path}") + # write the data file + path = os.path.join(folder, f"print_designer-{fname}-preview.json") + with open(path, "w+") as json_file: + json_file.write(frappe.as_json(file_export)) + print(f"Wrote document file for {file.doctype} {file.name} at {path}") diff --git a/print_designer/print_designer/page/print_designer/jinja/macros/image.html b/print_designer/print_designer/page/print_designer/jinja/macros/image.html index 7662b9f..0515361 100644 --- a/print_designer/print_designer/page/print_designer/jinja/macros/image.html +++ b/print_designer/print_designer/page/print_designer/jinja/macros/image.html @@ -1,14 +1,26 @@ {% macro image(element) -%} +{%- if element.image.file_url -%} + {%- set value = element.image.file_url -%} +{%- elif element.image.fieldname -%} + {%- if element.image.parent == doc.doctype -%} + {%- set value = doc.get(element.image.fieldname) -%} + {%- else -%} + {%- set value = frappe.db.get_value(element.image.doctype, doc[element.image.parentField], element.image.fieldname) -%} + {%- endif -%} +{%- else -%} + {%- set value = "" -%} +{%- endif -%} + +{%- if value -%}
+{%- endif -%} {%- endmacro %} \ No newline at end of file diff --git a/print_designer/print_designer/page/print_designer/jinja/old_print_format.html b/print_designer/print_designer/page/print_designer/jinja/old_print_format.html index 0a54666..8945ea4 100644 --- a/print_designer/print_designer/page/print_designer/jinja/old_print_format.html +++ b/print_designer/print_designer/page/print_designer/jinja/old_print_format.html @@ -40,6 +40,20 @@ {%- endmacro %} {% macro render_image(element) -%} + +{%- if element.image.file_url -%} + {%- set value = element.image.file_url -%} +{%- elif element.image.fieldname -%} + {%- if element.image.parent == doc.doctype -%} + {%- set value = doc.get(element.image.fieldname) -%} + {%- else -%} + {%- set value = frappe.db.get_value(element.image.doctype, doc[element.image.parentField], element.image.fieldname) -%} + {%- endif -%} +{%- else -%} + {%- set value = "" -%} +{%- endif -%} + +{%- if value -%}
+{%- endif -%} {%- endmacro %} {% macro render_barcode(element, send_to_jinja) -%} {%- set field = element.dynamicContent[0] -%} diff --git a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue index 4828bc3..c5065ce 100644 --- a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue +++ b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue @@ -465,13 +465,6 @@ onMounted(() => { watchEffect(() => { if (MainStore.screenStyleSheet) { - if (MainStore.screenStyleSheet.CssRuleIndex != null) { - try { - MainStore.screenStyleSheet.deleteRule(MainStore.screenStyleSheet.CssRuleIndex); - } catch (error) { - console.warn("Error Deleting Rule", error); - } - } MainStore.screenStyleSheet.CssRuleIndex = MainStore.addStylesheetRules([ [ ":root, ::after, ::before", @@ -530,13 +523,6 @@ onMounted(() => { }); watchEffect(() => { if (MainStore.printStyleSheet && MainStore.page) { - for (let index = 0; index < MainStore.printStyleSheet.cssRules.length; index++) { - try { - MainStore.printStyleSheet.deleteRule(index); - } catch (error) { - console.warn("Error Deleting Rule", error); - } - } const convertToMM = (input) => { let convertedUnit = useChangeValueUnit({ inputString: input, @@ -568,13 +554,6 @@ watchEffect(() => { }); watchEffect(() => { if (MainStore.screenStyleSheet && MainStore.modalLocation) { - for (let index = 0; index < MainStore.screenStyleSheet.cssRules.length; index++) { - try { - MainStore.screenStyleSheet.deleteRule(index); - } catch (error) { - console.warn("Error Deleting Rule", error); - } - } MainStore.addStylesheetRules([ [ ":root", diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index 40b8b51..eede374 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -9,6 +9,9 @@ import { createBarcode, } from "../defaultObjects"; import { handlePrintFonts, setCurrentElement } from "../utils"; + +import html2canvas from "html2canvas"; + export const useElementStore = defineStore("ElementStore", { state: () => ({ Elements: new Array(), @@ -34,10 +37,116 @@ export const useElementStore = defineStore("ElementStore", { } return newElement; }, + // This is modified version of upload function used in frappe/FileUploader.vue + async upload_file(file) { + const MainStore = useMainStore(); + MainStore.print_designer_preview_img = null; + return new Promise((resolve, reject) => { + let xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => { + if (xhr.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + try { + r = JSON.parse(xhr.responseText); + if (r.message.doctype === "File") { + file_url = r.message.file_url; + // if format is not saved then update the preview image value with it + if (MainStore.isFormatSaving) { + MainStore.print_designer_preview_img = file_url; + } else { + // if format is already saved then update the print_designer_preview_img field + frappe.db.set_value( + "Print Format", + MainStore.printDesignName, + "print_designer_preview_img", + file_url + ); + } + } + } catch (e) { + r = xhr.responseText; + } + } + } + }; + xhr.open("POST", "/api/method/upload_file", true); + xhr.setRequestHeader("Accept", "application/json"); + xhr.setRequestHeader("X-Frappe-CSRF-Token", frappe.csrf_token); + + let form_data = new FormData(); + if (file.file_obj) { + form_data.append("file", file.file_obj, file.name); + } + form_data.append("is_private", 1); + + form_data.append("doctype", "Print Format"); + form_data.append("docname", MainStore.printDesignName); + + form_data.append("fieldname", "print_designer_preview_img"); + + if (file.optimize) { + form_data.append("optimize", true); + } + xhr.send(form_data); + }); + }, + async generatePreview() { + const MainStore = useMainStore(); + // first delete old preview image + const filter = { + attached_to_doctype: "Print Format", + attached_to_name: MainStore.printDesignName, + attached_to_field: "print_designer_preview_img", + }; + // get filename before uploading new file + let old_filename = await frappe.db.get_value("File", filter, "name"); + old_filename = old_filename.message.name; + if (old_filename) { + frappe.db.delete_doc("File", old_filename); + } + + const options = { + backgroundColor: "#ffffff", + height: MainStore.page.height, + width: MainStore.page.width, + }; + const print_stylesheet = document.createElement("style"); + print_stylesheet.rel = "stylesheet"; + let st = `.main-container::after { + display: none; + }`; + document.getElementsByClassName("main-container")[0].appendChild(print_stylesheet); + print_stylesheet.sheet.insertRule(st, 0); + const preview_canvas = await html2canvas( + document.getElementsByClassName("main-container")[0], + options + ); + document.getElementsByClassName("main-container")[0].removeChild(print_stylesheet); + preview_canvas.toBlob(async (blob) => { + const file = new File( + [blob], + `print_designer-${frappe.scrub(MainStore.printDesignName)}-preview.jpg`, + { type: "image/jpeg" } + ); + const file_data = { + file_obj: file, + optimize: 1, + name: file.name, + private: true, + }; + await this.upload_file(file_data); + }); + }, async saveElements() { const MainStore = useMainStore(); if (this.checkIfAnyTableIsEmpty()) return; - + if (MainStore.mode == "preview") return; + let is_standard = await frappe.db.get_value( + "Print Format", + MainStore.printDesignName, + "standard" + ); + MainStore.is_standard = is_standard.message.standard == "Yes"; // Update the header and footer height with margin MainStore.page.headerHeightWithMargin = MainStore.page.headerHeight + MainStore.page.marginTop; @@ -62,6 +171,9 @@ export const useElementStore = defineStore("ElementStore", { const [cleanedBodyElements, bodyFonts] = body; const [cleanedHeaderElements, headerFonts] = header || [[], null]; const [cleanedFooterElements, footerFonts] = footer || [[], null]; + MainStore.printHeaderFonts = headerFonts; + MainStore.printBodyFonts = bodyFonts; + MainStore.printFooterFonts = footerFonts; MainStore.currentFonts.length = 0; MainStore.currentFonts.push( @@ -119,12 +231,17 @@ export const useElementStore = defineStore("ElementStore", { dimensions: footerDimensions, }, }); + MainStore.isFormatSaving = true; + + await this.generatePreview(); objectToSave.print_designer_print_format = PrintFormatData; + objectToSave.print_designer_preview_img = MainStore.print_designer_preview_img; if (MainStore.isOlderSchema("1.1.0")) { await this.printFormatCopyOnOlderSchema(objectToSave); } else { await frappe.db.set_value("Print Format", MainStore.printDesignName, objectToSave); + MainStore.isFormatSaving = false; frappe.show_alert( { message: `Print Format Saved Successfully`, @@ -322,12 +439,12 @@ export const useElementStore = defineStore("ElementStore", { }, cleanUpElementsForSave(elements, type) { if (this.checkIfPrintFormatIsEmpty(elements, type)) return; - const fontsArray = []; + const fontsObject = {}; const cleanedElements = []; elements.forEach((container) => { const cleanedContainer = []; container.forEach((element) => { - let newElement = this.childrensSave(element.element, fontsArray); + let newElement = this.childrensSave(element.element, fontsObject); newElement.classes = newElement.classes.filter( (name) => ["inHeaderFooter", "overlappingHeaderFooter"].indexOf(name) == -1 ); @@ -342,7 +459,7 @@ export const useElementStore = defineStore("ElementStore", { }); cleanedElements.push(cleanedContainer); }); - return [cleanedElements, fontsArray]; + return [cleanedElements, fontsObject]; }, checkIfPrintFormatIsEmpty(elements, type) { const MainStore = useMainStore(); @@ -377,6 +494,11 @@ export const useElementStore = defineStore("ElementStore", { delete saveEl.snapPoints; delete saveEl.snapEdges; delete saveEl.parent; + this.cleanUpDynamicContent(saveEl); + if (saveEl.type == "table") { + delete saveEl.table.childfields; + delete saveEl.table.default_layout; + } if (printFonts && ["text", "table"].indexOf(saveEl.type) != -1) { handlePrintFonts(saveEl, printFonts); } @@ -391,6 +513,55 @@ export const useElementStore = defineStore("ElementStore", { return saveEl; }, + cleanUpDynamicContent(element) { + const MainStore = useMainStore(); + if ( + ["table", "image"].includes(element.type) || + (["text", "barcode"].includes(element.type) && element.isDynamic) + ) { + if (["text", "barcode"].indexOf(element.type) != -1) { + element.dynamicContent = [ + ...element.dynamicContent.map((el) => { + const newEl = { ...el }; + if (!el.is_static) { + newEl.value = ""; + } + return newEl; + }), + ]; + element.selectedDynamicText = null; + } else if (element.type === "table") { + element.columns = [ + ...element.columns.map((el) => { + const newEl = { ...el }; + delete newEl.DOMRef; + return newEl; + }), + ]; + element.columns.forEach((col) => { + if (!col.dynamicContent) return; + col.dynamicContent = [ + ...col.dynamicContent.map((el) => { + const newEl = { ...el }; + if (!el.is_static) { + newEl.value = ""; + } + return newEl; + }), + ]; + col.selectedDynamicText = null; + }); + } else { + element.image = { ...element.image }; + if (MainStore.is_standard) { + // remove file_url and file_name if format is standard + ["value", "name", "file_name", "file_url", "modified"].forEach((key) => { + element.image[key] = ""; + }); + } + } + } + }, getPrintFormatData({ header, body, footer }) { const headerElements = this.createWrapperElement( header.elements, @@ -443,56 +614,100 @@ export const useElementStore = defineStore("ElementStore", { return wrapperContainers.childrens.map((el) => this.childrensSave(el)); }, async printFormatCopyOnOlderSchema(objectToSave) { - const MainStore = useMainStore(); - let nextFormatCopyNumber = 0; - for (let i = 0; i < 100; i++) { - const pf_exists = await frappe.db.exists( - "Print Format", - MainStore.printDesignName + " ( Copy " + (i ? i : "") + " )" - ); - if (pf_exists) continue; - nextFormatCopyNumber = i; - break; - } - const newName = - MainStore.printDesignName + - " ( Copy " + - (nextFormatCopyNumber ? nextFormatCopyNumber : "") + - " )"; // TODO: have better message. let message = __( "This Print Format was created from older version of Print Designer." ); message += "
"; message += __( - "It is not compatible with current version so instead we will make copy of this format for you using new version" + "It is not compatible with current version so instead make copy of this format using new version" ); message += "
"; - message += __(`Do you want to save it as ${newName} ?`); + message += __(`Do you want to save copy of it ?`); frappe.confirm( message, async () => { - await frappe.db.insert({ - doctype: "Print Format", - name: newName, - doc_type: MainStore.doctype, - print_designer: 1, - print_designer_header: objectToSave.print_designer_header, - print_designer_body: objectToSave.print_designer_body, - print_designer_after_table: null, - print_designer_footer: objectToSave.print_designer_footer, - print_designer_print_format: objectToSave.print_designer_print_format, - print_designer_settings: objectToSave.print_designer_settings, - }); - frappe.set_route("print-designer", newName); + this.promptUserForNewFormatName(objectToSave); }, async () => { + frappe.show_alert( + { + message: `Print Format not saved`, + indicator: "red", + }, + 5 + ); + // intentionally throwing error to stop the saving the format throw new Error(__("Print Format not saved")); } ); }, + async promptUserForNewFormatName(objectToSave) { + const MainStore = useMainStore(); + let nextFormatCopyNumber = 0; + for (let i = 0; i < 100; i++) { + const pf_exists = await frappe.db.exists( + "Print Format", + MainStore.printDesignName + " ( Copy " + (i ? i : "") + " )" + ); + if (pf_exists) continue; + nextFormatCopyNumber = i; + break; + } + // This is just default value for the new print format name + const print_format_name = + MainStore.printDesignName + + " ( Copy " + + (nextFormatCopyNumber ? nextFormatCopyNumber : "") + + " )"; + let d = new frappe.ui.Dialog({ + title: "New Print Format", + fields: [ + { + label: "Name", + fieldname: "print_format_name", + fieldtype: "Data", + reqd: 1, + default: print_format_name, + }, + ], + size: "small", + primary_action_label: "Save", + static: true, + async primary_action(values) { + try { + await frappe.db.insert({ + doctype: "Print Format", + name: values.print_format_name, + doc_type: MainStore.doctype, + print_designer: 1, + print_designer_header: objectToSave.print_designer_header, + print_designer_body: objectToSave.print_designer_body, + print_designer_after_table: null, + print_designer_footer: objectToSave.print_designer_footer, + print_designer_print_format: objectToSave.print_designer_print_format, + print_designer_settings: objectToSave.print_designer_settings, + }); + d.hide(); + frappe.set_route("print-designer", values.print_format_name); + } catch (error) { + console.error(error); + } + }, + }); + d.get_close_btn().on("click", () => { + frappe.show_alert( + { + message: `Print Format not saved`, + indicator: "red", + }, + 5 + ); + }); + d.show(); + }, handleDynamicContent(element) { const MainStore = useMainStore(); if ( @@ -502,12 +717,25 @@ export const useElementStore = defineStore("ElementStore", { if (["text", "barcode"].indexOf(element.type) != -1) { element.dynamicContent = [ ...element.dynamicContent.map((el) => { - return { ...el }; + const newEl = { ...el }; + if (!el.is_static) { + newEl.value = ""; + } + return newEl; }), ]; element.selectedDynamicText = null; MainStore.dynamicData.push(...element.dynamicContent); } else if (element.type === "table") { + if (element.table) { + const mf = MainStore.metaFields.find( + (field) => field.fieldname == element.table.fieldname + ); + if (mf) { + element.table = mf; + } + } + element.columns = [ ...element.columns.map((el) => { return { ...el }; @@ -517,7 +745,11 @@ export const useElementStore = defineStore("ElementStore", { if (!col.dynamicContent) return; col.dynamicContent = [ ...col.dynamicContent.map((el) => { - return { ...el }; + const newEl = { ...el }; + if (!el.is_static) { + newEl.value = ""; + } + return newEl; }), ]; col.selectedDynamicText = null; diff --git a/yarn.lock b/yarn.lock index 8f1cafd..8893f97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -106,6 +106,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -143,6 +148,13 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + debug@4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -216,6 +228,14 @@ has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -451,6 +471,13 @@ tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -461,6 +488,13 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + web-streams-polyfill@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"