diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json
index 4032cef209..52220ed67c 100644
--- a/frappe/printing/doctype/print_format/print_format.json
+++ b/frappe/printing/doctype/print_format/print_format.json
@@ -19,6 +19,10 @@
"html",
"raw_commands",
"section_break_9",
+ "margin_top",
+ "margin_bottom",
+ "margin_left",
+ "margin_right",
"align_labels_right",
"show_section_headings",
"line_breaks",
@@ -31,7 +35,8 @@
"section_break_13",
"print_format_help",
"format_data",
- "print_format_builder"
+ "print_format_builder",
+ "print_format_builder_beta"
],
"fields": [
{
@@ -205,13 +210,43 @@
"fieldname": "absolute_value",
"fieldtype": "Check",
"label": "Show Absolute Values"
+ },
+ {
+ "default": "0",
+ "fieldname": "print_format_builder_beta",
+ "fieldtype": "Check",
+ "label": "Print Format Builder Beta"
+ },
+ {
+ "default": "15",
+ "fieldname": "margin_top",
+ "fieldtype": "Float",
+ "label": "Margin Top"
+ },
+ {
+ "default": "15",
+ "fieldname": "margin_bottom",
+ "fieldtype": "Float",
+ "label": "Margin Bottom"
+ },
+ {
+ "default": "15",
+ "fieldname": "margin_left",
+ "fieldtype": "Float",
+ "label": "Margin Left"
+ },
+ {
+ "default": "15",
+ "fieldname": "margin_right",
+ "fieldtype": "Float",
+ "label": "Margin Right"
}
],
"icon": "fa fa-print",
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-03-01 15:25:46.578863",
+ "modified": "2021-07-11 11:53:52.028982",
"modified_by": "Administrator",
"module": "Printing",
"name": "Print Format",
diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py
index 878a864b38..2049c4105c 100644
--- a/frappe/printing/doctype/print_format/print_format.py
+++ b/frappe/printing/doctype/print_format/print_format.py
@@ -38,6 +38,10 @@ class PrintFormat(Document):
def extract_images(self):
from frappe.core.doctype.file.file import extract_images_from_html
+
+ if self.print_format_builder_beta:
+ return
+
if self.format_data:
data = json.loads(self.format_data)
for df in data:
diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js
index ca2a340661..225c06980e 100644
--- a/frappe/printing/page/print/print.js
+++ b/frappe/printing/page/print/print.js
@@ -258,6 +258,11 @@ frappe.ui.form.PrintView = class {
fieldtype: 'Read Only',
default: print_format.name || 'Standard',
},
+ {
+ label: __('Use the new Print Format Builder Beta'),
+ fieldname: 'beta',
+ fieldtype: 'Check'
+ },
],
(data) => {
frappe.route_options = {
@@ -265,6 +270,7 @@ frappe.ui.form.PrintView = class {
doctype: this.frm.doctype,
name: data.print_format_name,
based_on: data.based_on,
+ beta: data.beta
};
frappe.set_route('print-format-builder');
this.print_sel.val(data.print_format_name);
diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js
index ca2a8bc378..e2d00bd53d 100644
--- a/frappe/printing/page/print_format_builder/print_format_builder.js
+++ b/frappe/printing/page/print_format_builder/print_format_builder.js
@@ -12,9 +12,9 @@ frappe.pages['print-format-builder'].on_page_show = function(wrapper) {
});
} else if(frappe.route_options) {
if(frappe.route_options.make_new) {
- let { doctype, name, based_on } = frappe.route_options;
+ let { doctype, name, based_on, beta } = frappe.route_options;
frappe.route_options = null;
- frappe.print_format_builder.setup_new_print_format(doctype, name, based_on);
+ frappe.print_format_builder.setup_new_print_format(doctype, name, based_on, beta);
} else {
frappe.print_format_builder.print_format = frappe.route_options.doc;
frappe.route_options = null;
@@ -126,18 +126,22 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder {
});
}
- setup_new_print_format(doctype, name, based_on) {
+ setup_new_print_format(doctype, name, based_on, beta) {
frappe.call({
method: 'frappe.printing.page.print_format_builder.print_format_builder.create_custom_format',
args: {
doctype: doctype,
name: name,
- based_on: based_on
+ based_on: based_on,
+ beta: Boolean(beta)
},
callback: (r) => {
- if(!r.exc) {
- if(r.message) {
- this.print_format = r.message;
+ if(r.message) {
+ let print_format = r.message;
+ if (print_format.print_format_builder_beta) {
+ frappe.set_route('print-format-builder-beta', print_format.name);
+ } else {
+ this.print_format = print_format;
this.refresh();
}
}
diff --git a/frappe/printing/page/print_format_builder/print_format_builder.py b/frappe/printing/page/print_format_builder/print_format_builder.py
index d9f57762b0..fae564d3c3 100644
--- a/frappe/printing/page/print_format_builder/print_format_builder.py
+++ b/frappe/printing/page/print_format_builder/print_format_builder.py
@@ -1,11 +1,16 @@
import frappe
@frappe.whitelist()
-def create_custom_format(doctype, name, based_on='Standard'):
+def create_custom_format(doctype, name, based_on='Standard', beta=False):
doc = frappe.new_doc('Print Format')
doc.doc_type = doctype
doc.name = name
- doc.print_format_builder = 1
+ beta = frappe.parse_json(beta)
+
+ if beta:
+ doc.print_format_builder_beta = 1
+ else:
+ doc.print_format_builder = 1
doc.format_data = frappe.db.get_value('Print Format', based_on, 'format_data') \
if based_on != 'Standard' else None
doc.insert()
diff --git a/frappe/printing/page/print_format_builder_beta/__init__.py b/frappe/printing/page/print_format_builder_beta/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css
new file mode 100644
index 0000000000..0bd8d9c0f3
--- /dev/null
+++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.css
@@ -0,0 +1,3 @@
+.layout-main-section-wrapper {
+ margin-bottom: 0;
+}
diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js
new file mode 100644
index 0000000000..9004dd8b98
--- /dev/null
+++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.js
@@ -0,0 +1,31 @@
+frappe.pages["print-format-builder-beta"].on_page_load = function(wrapper) {
+ var page = frappe.ui.make_app_page({
+ parent: wrapper,
+ title: __("Print Format Builder"),
+ single_column: true
+ });
+
+ function load_print_format_builder_beta() {
+ let route = frappe.get_route();
+ let $parent = $(wrapper).find(".layout-main-section");
+ $parent.empty();
+
+ if (route.length > 1) {
+ frappe.require("print_format_builder.bundle.js").then(() => {
+ frappe.print_format_builder = new frappe.ui.PrintFormatBuilder({
+ wrapper: $parent,
+ page,
+ print_format: route[1]
+ });
+ });
+ }
+ }
+
+ load_print_format_builder_beta();
+
+ // hot reload in development
+ if (frappe.boot.developer_mode) {
+ frappe.hot_update = frappe.hot_update || [];
+ frappe.hot_update.push(load_print_format_builder_beta);
+ }
+};
diff --git a/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json
new file mode 100644
index 0000000000..a5b1288bc0
--- /dev/null
+++ b/frappe/printing/page/print_format_builder_beta/print_format_builder_beta.json
@@ -0,0 +1,22 @@
+{
+ "content": null,
+ "creation": "2021-07-10 12:22:16.138485",
+ "docstatus": 0,
+ "doctype": "Page",
+ "idx": 0,
+ "modified": "2021-07-10 12:22:16.138485",
+ "modified_by": "Administrator",
+ "module": "Printing",
+ "name": "print-format-builder-beta",
+ "owner": "Administrator",
+ "page_name": "Print Format Builder Beta",
+ "roles": [
+ {
+ "role": "System Manager"
+ }
+ ],
+ "script": null,
+ "standard": "Yes",
+ "style": null,
+ "system_page": 0
+}
\ No newline at end of file
diff --git a/frappe/public/js/print_format_builder/PrintFormat.vue b/frappe/public/js/print_format_builder/PrintFormat.vue
new file mode 100644
index 0000000000..9e8e2033a4
--- /dev/null
+++ b/frappe/public/js/print_format_builder/PrintFormat.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frappe/public/js/print_format_builder/PrintFormatBuilder.vue b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue
new file mode 100644
index 0000000000..88b70f8b1b
--- /dev/null
+++ b/frappe/public/js/print_format_builder/PrintFormatBuilder.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 0000000000..b4f452751a
--- /dev/null
+++ b/frappe/public/js/print_format_builder/PrintFormatControls.vue
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frappe/public/js/print_format_builder/PrintFormatSection.vue b/frappe/public/js/print_format_builder/PrintFormatSection.vue
new file mode 100644
index 0000000000..e204c2053f
--- /dev/null
+++ b/frappe/public/js/print_format_builder/PrintFormatSection.vue
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 0000000000..e364bfa29e
--- /dev/null
+++ b/frappe/public/js/print_format_builder/print_format_builder.bundle.js
@@ -0,0 +1,31 @@
+import PrintFormatBuilderComponent from "./PrintFormatBuilder.vue";
+
+class PrintFormatBuilder {
+ constructor({ wrapper, page, print_format }) {
+ this.$wrapper = $(wrapper);
+ this.page = page;
+ this.print_format = print_format;
+ this.page.set_title(__("Editing {0}", [this.print_format]));
+ this.page.set_primary_action(__("Save changes"), () => {
+ this.$component.save_changes();
+ });
+ this.page.set_secondary_action(__("Reset changes"), () => {
+ this.$component.reset_changes();
+ });
+
+ let $vm = new Vue({
+ el: this.$wrapper.get(0),
+ render: h =>
+ h(PrintFormatBuilderComponent, {
+ props: {
+ print_format_name: print_format
+ }
+ })
+ });
+ this.$component = $vm.$children[0];
+ }
+}
+
+frappe.provide("frappe.ui");
+frappe.ui.PrintFormatBuilder = PrintFormatBuilder;
+export default PrintFormatBuilder;
diff --git a/frappe/public/js/print_format_builder/utils.js b/frappe/public/js/print_format_builder/utils.js
new file mode 100644
index 0000000000..9428ca61f9
--- /dev/null
+++ b/frappe/public/js/print_format_builder/utils.js
@@ -0,0 +1,100 @@
+export function create_default_layout(meta) {
+ let layout = {
+ sections: []
+ };
+
+ let section = null,
+ column = null;
+
+ function set_column(df) {
+ if (!section) {
+ set_section();
+ }
+ column = get_new_column(df);
+ section.columns.push(column);
+ }
+
+ function set_section(df) {
+ section = get_new_section(df);
+ column = null;
+ layout.sections.push(section);
+ }
+
+ function get_new_section(df) {
+ if (!df) {
+ df = { label: "" };
+ }
+ return {
+ label: df.label || "",
+ columns: []
+ };
+ }
+
+ function get_new_column(df) {
+ if (!df) {
+ df = { label: "" };
+ }
+ return {
+ label: df.label || "",
+ fields: []
+ };
+ }
+
+ for (let df of meta.fields) {
+ if (df.fieldname) {
+ // make a copy to avoid mutation bugs
+ df = JSON.parse(JSON.stringify(df));
+ } else {
+ continue;
+ }
+
+ if (df.fieldtype === "Section Break") {
+ set_section(df);
+ } else if (df.fieldtype === "Column Break") {
+ set_column(df);
+ } else if (df.label) {
+ if (!column) set_column();
+
+ if (!df.print_hide) {
+ let field = {
+ label: df.label,
+ fieldname: df.fieldname,
+ options: df.options
+ };
+
+ if (df.fieldtype === "Table") {
+ field.table_columns = get_table_columns(df);
+ }
+
+ column.fields.push(field);
+ section.has_fields = true;
+ }
+ }
+ }
+
+ // remove empty sections
+ layout.sections = layout.sections.filter(section => section.has_fields);
+
+ return layout;
+}
+
+export function get_table_columns(df) {
+ let table_columns = [];
+ let table_fields = frappe.get_meta(df.options).fields;
+
+ for (let tf of table_fields) {
+ if (
+ !in_list(["Section Break", "Column Break"], tf.fieldtype) &&
+ !tf.print_hide &&
+ df.label
+ ) {
+ table_columns.push({
+ label: tf.label,
+ fieldname: tf.fieldname,
+ options: tf.options,
+ width: tf.width || 0
+ });
+ }
+ }
+ return table_columns;
+}
diff --git a/package.json b/package.json
index 2283a44533..82a335c2ba 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,8 @@
"superagent": "^3.8.2",
"touch": "^3.1.0",
"vue": "2.6.12",
- "vue-router": "^2.0.0"
+ "vue-router": "^2.0.0",
+ "vuedraggable": "^2.24.3"
},
"devDependencies": {
"chalk": "^2.3.2",
diff --git a/yarn.lock b/yarn.lock
index ee530d747b..cdabdb6565 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6911,6 +6911,11 @@ socket.io@^2.4.0:
socket.io-client "2.4.0"
socket.io-parser "~3.4.0"
+sortablejs@1.10.2:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
+ integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
+
sortablejs@^1.7.0:
version "1.8.3"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df"
@@ -7790,6 +7795,13 @@ vue@2.6.12:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
+vuedraggable@^2.24.3:
+ version "2.24.3"
+ resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
+ integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
+ dependencies:
+ sortablejs "1.10.2"
+
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"