+
@@ -16,87 +12,33 @@
@@ -107,4 +49,4 @@ export default {
padding-top: 0.5rem;
padding-bottom: 4rem;
}
-
\ No newline at end of file
+
diff --git a/frappe/public/js/print_format_builder/PrintFormatControls.vue b/frappe/public/js/print_format_builder/PrintFormatControls.vue
index b4f452751a..dd54f8f55e 100644
--- a/frappe/public/js/print_format_builder/PrintFormatControls.vue
+++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue
@@ -14,7 +14,7 @@
type="number"
class="form-control form-control-sm"
:value="print_format[df.fieldname]"
- @change="(e) => update_margin(df.fieldname, e.target.value)"
+ @change="e => update_margin(df.fieldname, e.target.value)"
/>
@@ -24,7 +24,7 @@
@@ -74,19 +62,23 @@
@@ -166,38 +158,8 @@ export default {
padding-right: 8px;
}
-.field {
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- background-color: var(--bg-light-gray);
- border-radius: var(--border-radius);
- border: 1px dashed var(--gray-400);
- padding: 0.5rem 0.75rem;
- font-size: var(--text-sm);
-}
-
-.field:not(:first-child) {
- margin-top: 0.5rem;
-}
-
-.btn-remove-field {
- opacity: 0;
- padding: 2px;
- box-shadow: none;
-}
-
-.btn-remove-field:hover {
- background-color: white;
-}
-
-.field:hover .btn-remove-field {
- opacity: 1;
-}
-
.drag-container {
height: 100%;
min-height: 2rem;
}
-
\ No newline at end of file
+
diff --git a/frappe/public/js/print_format_builder/print_format_builder.bundle.js b/frappe/public/js/print_format_builder/print_format_builder.bundle.js
index e364bfa29e..63cc098380 100644
--- a/frappe/public/js/print_format_builder/print_format_builder.bundle.js
+++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js
@@ -5,13 +5,24 @@ class PrintFormatBuilder {
this.$wrapper = $(wrapper);
this.page = page;
this.print_format = print_format;
+
+ this.page.clear_actions()
+ this.page.clear_custom_actions()
+
this.page.set_title(__("Editing {0}", [this.print_format]));
this.page.set_primary_action(__("Save changes"), () => {
- this.$component.save_changes();
+ this.$component.$store.save_changes();
});
this.page.set_secondary_action(__("Reset changes"), () => {
- this.$component.reset_changes();
+ this.$component.$store.reset_changes();
});
+ this.page.add_button(
+ __("Preview"),
+ () => {
+ this.preview();
+ },
+ { icon: "small-file" }
+ );
let $vm = new Vue({
el: this.$wrapper.get(0),
@@ -24,6 +35,46 @@ class PrintFormatBuilder {
});
this.$component = $vm.$children[0];
}
+
+ async preview() {
+ let doctype = this.$component.$store.print_format.doc_type;
+ let default_doc = await frappe.db.get_list(doctype, {
+ limit: 1
+ });
+
+ let d = new frappe.ui.Dialog({
+ title: __("Preview Print Format"),
+ fields: [
+ {
+ label: __("Type"),
+ fieldname: "type",
+ fieldtype: "Select",
+ options: ["PDF", "HTML"],
+ default: "PDF"
+ },
+ {
+ label: __("Select Document"),
+ fieldname: "docname",
+ fieldtype: "Link",
+ options: doctype,
+ reqd: 1,
+ default: default_doc.length > 0 ? default_doc[0].name : null
+ }
+ ],
+ primary_action: ({ docname, type }) => {
+ let params = new URLSearchParams();
+ params.append("doctype", doctype);
+ params.append("name", docname);
+ params.append("print_format", this.print_format);
+ let url =
+ type == "PDF"
+ ? `/api/method/frappe.utils.weasyprint.download_pdf`
+ : "/printpreview";
+ window.open(`${url}?${params.toString()}`, "_blank");
+ }
+ });
+ d.show();
+ }
}
frappe.provide("frappe.ui");
diff --git a/frappe/public/js/print_format_builder/store.js b/frappe/public/js/print_format_builder/store.js
new file mode 100644
index 0000000000..3af4a21dec
--- /dev/null
+++ b/frappe/public/js/print_format_builder/store.js
@@ -0,0 +1,125 @@
+import { create_default_layout, pluck } from "./utils";
+
+let stores = {};
+
+export function getStore(print_format_name) {
+ if (stores[print_format_name]) {
+ return stores[print_format_name];
+ }
+
+ let options = {
+ data() {
+ return {
+ print_format_name,
+ print_format: null,
+ doctype: null,
+ meta: null,
+ layout: null
+ };
+ },
+ methods: {
+ fetch() {
+ frappe.model.clear_doc("Print Format", this.print_format_name);
+ frappe.model.with_doc(
+ "Print Format",
+ this.print_format_name,
+ () => {
+ this.print_format = frappe.get_doc(
+ "Print Format",
+ this.print_format_name
+ );
+ frappe.model.with_doctype(
+ this.print_format.doc_type,
+ () => {
+ this.meta = frappe.get_meta(
+ this.print_format.doc_type
+ );
+ this.layout = this.get_layout();
+ }
+ );
+ }
+ );
+ },
+ update({ fieldname, value }) {
+ this.$set(this.print_format, fieldname, value);
+ },
+ save_changes() {
+ frappe.dom.freeze(__("Saving..."));
+
+ this.layout.sections = this.layout.sections
+ .filter(section => !section.remove)
+ .map(section => {
+ section.columns = section.columns.map(column => {
+ column.fields = column.fields
+ .filter(df => !df.remove)
+ .map(df => {
+ if (df.table_columns) {
+ df.table_columns = df.table_columns.map(
+ tf => {
+ return pluck(tf, [
+ "label",
+ "fieldname",
+ "fieldtype",
+ "options",
+ "width"
+ ]);
+ }
+ );
+ }
+ return pluck(df, [
+ "label",
+ "fieldname",
+ "fieldtype",
+ "options",
+ "table_columns"
+ ]);
+ });
+ return column;
+ });
+ return section;
+ });
+
+ this.print_format.format_data = JSON.stringify(this.layout);
+
+ frappe
+ .call("frappe.client.save", {
+ doc: this.print_format
+ })
+ .then(() => this.fetch())
+ .always(() => frappe.dom.unfreeze());
+ },
+ reset_changes() {
+ this.fetch();
+ },
+ get_layout() {
+ if (this.print_format) {
+ if (!this.print_format.format_data) {
+ return create_default_layout(this.meta);
+ }
+ if (typeof this.print_format.format_data == "string") {
+ return JSON.parse(this.print_format.format_data);
+ }
+ return this.print_format.format_data;
+ }
+ return null;
+ }
+ }
+ };
+ stores[print_format_name] = new Vue(options);
+ return stores[print_format_name];
+}
+
+export let storeMixin = {
+ inject: ["$store"],
+ computed: {
+ print_format() {
+ return this.$store.print_format;
+ },
+ layout() {
+ return this.$store.layout;
+ },
+ meta() {
+ return this.$store.meta;
+ }
+ }
+};
diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js
index 9428ca61f9..7bdd78a04a 100644
--- a/frappe/public/js/print_format_builder/utils.js
+++ b/frappe/public/js/print_format_builder/utils.js
@@ -59,6 +59,7 @@ export function create_default_layout(meta) {
let field = {
label: df.label,
fieldname: df.fieldname,
+ fieldtype: df.fieldtype,
options: df.options
};
@@ -81,20 +82,35 @@ export function create_default_layout(meta) {
export function get_table_columns(df) {
let table_columns = [];
let table_fields = frappe.get_meta(df.options).fields;
-
+ let total_columns = 0;
for (let tf of table_fields) {
if (
!in_list(["Section Break", "Column Break"], tf.fieldtype) &&
!tf.print_hide &&
- df.label
+ df.label &&
+ total_columns < 12
) {
+ let columns =
+ typeof tf.width == "number" && tf.width < 12 ? tf.width : 2;
table_columns.push({
label: tf.label,
fieldname: tf.fieldname,
+ fieldtype: tf.fieldtype,
options: tf.options,
- width: tf.width || 0
+ width: columns
});
+ total_columns += columns;
}
}
return table_columns;
}
+
+export function pluck(object, keys) {
+ let out = {};
+ for (let key of keys) {
+ if (key in object) {
+ out[key] = object[key];
+ }
+ }
+ return out;
+}