diff --git a/check_run/check_run/doctype/check_run/check_run.py b/check_run/check_run/doctype/check_run/check_run.py index 5ed3e6e5..33e4beeb 100644 --- a/check_run/check_run/doctype/check_run/check_run.py +++ b/check_run/check_run/doctype/check_run/check_run.py @@ -20,6 +20,7 @@ from frappe.contacts.doctype.address.address import get_default_address from frappe.query_builder.custom import ConstantColumn from frappe.query_builder.functions import Coalesce, Sum +from frappe.desk.form.load import get_attachments from erpnext.accounts.utils import get_balance_on from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry @@ -607,6 +608,12 @@ def get_entries(doc: CheckRun | str) -> dict: query, {"company": company, "pay_to_account": pay_to_account, "end_date": end_date}, as_dict=True ) for transaction in transactions: + doc_name = transaction.ref_number if transaction.ref_number else transaction.name + transaction.attachments = [ + attachment for attachment in get_attachments(transaction.doctype, doc_name) + if attachment.file_url.endswith('.pdf') + ] or [{'file_name': doc_name, 'file_url': f'/app/Form/{transaction.doctype}/{doc_name}'}] + if settings and settings.pre_check_overdue_items: if transaction.due_date < doc.posting_date: transaction.pay = 1 diff --git a/check_run/check_run/report/__init__.py b/check_run/check_run/report/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/check_run/check_run/report/payables_attachments/__init__.py b/check_run/check_run/report/payables_attachments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/check_run/check_run/report/payables_attachments/payables_attachments.js b/check_run/check_run/report/payables_attachments/payables_attachments.js new file mode 100644 index 00000000..c71ad7eb --- /dev/null +++ b/check_run/check_run/report/payables_attachments/payables_attachments.js @@ -0,0 +1,13 @@ +// Copyright (c) 2023, AgriTheory and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports['Payables Attachments'] = { + filters: [], +} + +frappe.ui.addFilePreviewWrapper() + +function pdf_preview(file_url) { + frappe.ui.pdfPreview(cur_frm, file_url) +} diff --git a/check_run/check_run/report/payables_attachments/payables_attachments.json b/check_run/check_run/report/payables_attachments/payables_attachments.json new file mode 100644 index 00000000..96f252df --- /dev/null +++ b/check_run/check_run/report/payables_attachments/payables_attachments.json @@ -0,0 +1,35 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-08-11 10:09:28.938669", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2023-08-11 10:09:28.938669", + "modified_by": "Administrator", + "module": "Check Run", + "name": "Payables Attachments", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Invoice", + "report_name": "Payables Attachments", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Purchase User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/check_run/check_run/report/payables_attachments/payables_attachments.py b/check_run/check_run/report/payables_attachments/payables_attachments.py new file mode 100644 index 00000000..b479a7be --- /dev/null +++ b/check_run/check_run/report/payables_attachments/payables_attachments.py @@ -0,0 +1,145 @@ +# Copyright (c) 2022, AgriTheory and contributors +# For license information, please see license.txt + +import frappe +from frappe.desk.form.load import get_attachments +from frappe.query_builder import DocType +from pypika import Order + + +def execute(filters=None): + return get_columns(filters), get_data(filters) + +def get_data(filters): + PurchaseInvoice = DocType("Purchase Invoice") + data = ( + frappe.qb.from_(PurchaseInvoice) + .select( + PurchaseInvoice.name, PurchaseInvoice.title, PurchaseInvoice.supplier, PurchaseInvoice.company, + PurchaseInvoice.posting_date, PurchaseInvoice.grand_total, PurchaseInvoice.status, PurchaseInvoice.currency, + PurchaseInvoice.supplier_name, PurchaseInvoice.grand_total, PurchaseInvoice.outstanding_amount, + PurchaseInvoice.due_date, PurchaseInvoice.is_return, PurchaseInvoice.release_date, PurchaseInvoice.represents_company, + PurchaseInvoice.is_internal_supplier + ) + .orderby('modified', Order.desc) + ).run(as_dict=True) + + for row in data: + row['attachments'] = " ".join([ + f"""{attachment.file_name}""" + for attachment in get_attachments('Purchase Invoice', row['name']) if attachment.file_url.endswith('.pdf') + ]) + return data + +def get_columns(filters): + return [ + + { + "label": frappe._("Name"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Purchase Invoice", + "width": "150px", + }, + { + "label": frappe._("Title"), + "fieldname": "title", + "fieldtype": "Data", + "width": "200px", + }, + { + "label": frappe._("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": "200px", + }, + { + "label": frappe._("Supplier Name"), + "fieldname": "supplier_name", + "fieldtype": "Data", + "width": "200px", + }, + { + "label": frappe._("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": "200px", + }, + { + "label": frappe._("Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": "200px", + }, + { + "label": frappe._("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Currency", + "width": "200px", + }, + { + "label": frappe._("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": "200px", + }, + { + "label": frappe._("Currency"), + "fieldname": "currency", + "fieldtype": "Link", + "options": "Currency", + "width": "200px", + }, + { + "label": frappe._("Grand Total (Company Currency)"), + "fieldname": "grand_total", + "fieldtype": "Currency", + "width": "200px", + }, + { + "label": frappe._("Outstanding Amount"), + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "width": "200px", + }, + { + "label": frappe._("Due Date"), + "fieldname": "due_date", + "fieldtype": "Date", + "width": "200px", + }, + { + "label": frappe._("Is Return (Debit Note)"), + "fieldname": "is_return", + "fieldtype": "Check", + "width": "200px", + }, + { + "label": frappe._("Release Date"), + "fieldname": "release_date", + "fieldtype": "Date", + "width": "200px", + }, + { + "label": frappe._("Represents Company"), + "fieldname": "represents_company", + "fieldtype": "Link", + "options": "Company", + "width": "200px", + }, + { + "label": frappe._("Is Internal Supplier"), + "fieldname": "is_internal_supplier", + "fieldtype": "Check", + "width": "200px", + }, + { + "label": frappe._("Attachments"), + "fieldname": "attachments", + "fieldtype": "Data", + "width": "400px", + }, + ] + diff --git a/check_run/hooks.py b/check_run/hooks.py index 361a6e6a..b829b54d 100644 --- a/check_run/hooks.py +++ b/check_run/hooks.py @@ -18,6 +18,8 @@ app_include_js = [ "check_run.bundle.js", ] +app_include_css = "/assets/check_run/css/file_preview.css" + # include js, css files in header of web template # web_include_css = "/assets/check_run/css/check_run.css" diff --git a/check_run/public/css/file_preview.css b/check_run/public/css/file_preview.css new file mode 100644 index 00000000..5d577a8d --- /dev/null +++ b/check_run/public/css/file_preview.css @@ -0,0 +1,61 @@ +.fa-file-pdf-o { + font-size: 120%; + padding-left: 0.5ch; + color: var(--brand-secondary); +} +#close-pdf-button { + position: absolute; + top: 10px; + right: 10px; +} +#pdf-preview-wrapper { + position: fixed; + width: calc(1290px / 2); + height: calc(100vh - 135px); + top: 135px; + right: calc((100vw - 1290px) / 2); + display: none; +} +#pdf-preview-wrapper.pdf-preview-wrapper-fw { + width: calc(90vw / 2); + height: calc(100vh - 135px); + right: calc(10vw / 2); +} +#pdf-preview-wrapper.pdf-preview-wrapper-fw #pdf-preview { + width: calc(90vw / 2); +} +.page-body.show-pdf-preview #pdf-preview-wrapper { + display: block; +} +#pdf-preview { + position: absolute; + top: 50px; + left: 0; + right: 0; + bottom: 0; + width: calc(1290px / 2); + height: calc(100vh - 135px - 20px); +} +.page-body.show-pdf-preview .page-wrapper .page-content { + width: calc(50% - 10px); +} + +.portal-widget { + position: absolute; + top: 5px; + right:5px; + bottom:5px; + left:5px; + border-radius: 5px; + box-shadow: 0px 1px 2px rgba(25, 39, 52, 0.05), 0px 0px 4px rgba(25, 39, 52, 0.1); +} +a.portal-widget-col { + position: relative; + padding: 20px; + text-decoration: none; +} + +.widget-row { + margin-top: 10px; + margin-bottom: 20px; +} \ No newline at end of file diff --git a/check_run/public/js/check_run.bundle.js b/check_run/public/js/check_run.bundle.js index f81c5c24..24c936cb 100644 --- a/check_run/public/js/check_run.bundle.js +++ b/check_run/public/js/check_run.bundle.js @@ -1,2 +1,3 @@ import './check_run/check_run_quick_entry.js' import './check_run/check_run.js' +import './custom/file_preview.js' diff --git a/check_run/public/js/check_run/CheckRun.vue b/check_run/public/js/check_run/CheckRun.vue index a7038d1a..adda310b 100644 --- a/check_run/public/js/check_run/CheckRun.vue +++ b/check_run/public/js/check_run/CheckRun.vue @@ -43,7 +43,7 @@ >⬍ - + Select All - Check Number | Reference + Check Number | Reference @@ -65,10 +65,22 @@ tabindex="1" @click="state.selectedRow = i"> {{ item.party_name || item.party }} - + {{ item.ref_number || item.name }} + + + {{ item.posting_date }} @@ -198,6 +210,11 @@ export default { } this.$refs.dropdowns[this.state.selectedRow].openWithSearch() }, + showPreview(attachment) { + var file_url = typeof attachment == 'string' ? attachment : attachment[0].file_url + frappe.ui.addFilePreviewWrapper() + frappe.ui.pdfPreview(cur_frm, file_url) + } }, beforeMount() { this.moment = moment diff --git a/check_run/public/js/custom/file_preview.js b/check_run/public/js/custom/file_preview.js new file mode 100644 index 00000000..21bc07d7 --- /dev/null +++ b/check_run/public/js/custom/file_preview.js @@ -0,0 +1,98 @@ +frappe.provide('frappe.ui.form') + +$(document).on('page-change', function (e) { + frappe.ui.closeFilePreview() +}) + +$(document).on('click', function (e) { + if (!$(e.target).get(0).hasAttribute('data-pdf-preview')) { + frappe.ui.closeFilePreview() + } +}) + +$(document).on('keydown', e => { + if (e.which === frappe.ui.keyCode.ESCAPE) { + frappe.ui.closeFilePreview() + } + if (e.which === frappe.ui.keyCode.SPACE && cur_frm && cur_frm.doctype == 'Check Run') { + frappe.ui.closeFilePreview() + } +}) + +$('#close-pdf-button').on('click', event => { + frappe.ui.closeFilePreview() +}) + +frappe.ui.form.Attachments.prototype.add_attachment = attachment => { + const me = frappe.ui.form.Attachments.prototype + let file_name = attachment.file_name + var file_url = me.get_file_url(attachment) + var fileid = attachment.name + if (!file_name) { + file_name = file_url + } + let file_label = ` + + ${file_name} + ` + let remove_action = null + if (frappe.model.can_write(cur_frm.doctype, cur_frm.name)) { + remove_action = function (target_id) { + frappe.confirm(__('Are you sure you want to delete the attachment?'), function () { + cur_frm.attachments.remove_attachment(target_id) + }) + return false + } + } + + let icon = ` + ${frappe.utils.icon(attachment.is_private ? 'lock' : 'unlock', 'sm ml-0')} + ` + + if (file_name.endsWith('.pdf')) { + icon += `` + } + + $(`
  • `) + .append(frappe.get_data_pill(file_label, fileid, remove_action, icon)) + .insertAfter(cur_frm.attachments.attachments_label.addClass('has-attachments')) + + $('.fa-file-pdf-o').on('click', event => { + frappe.ui.pdfPreview(cur_frm, event.currentTarget.dataset.pdfPreview) + }) + + if (file_name.endsWith('.pdf')) { + frappe.ui.addFilePreviewWrapper() + } +} + +frappe.ui.addFilePreviewWrapper = () => { + if ($('#pdf-preview-wrapper').length == 0) { + $('.page-body .page-wrapper').append(`
    + +
    `) + } +} + +frappe.ui.closeFilePreview = () => { + if ($('#pdf-preview').length != 0) { + $('#pdf-preview').remove() + $('.page-body').removeClass('show-pdf-preview') + if (cur_frm !== null && cur_frm.doctype != 'Check Run') { + cur_frm.page.wrapper.find('.layout-side-section').show() + } + } +} + +frappe.ui.pdfPreview = (frm, file_url) => { + if (frm !== null) { + frm.page.wrapper.find('.layout-side-section').hide() + } + if (localStorage.container_fullwidth != 'false') { + $('#pdf-preview-wrapper').addClass('pdf-preview-wrapper-fw') + } else { + $('#pdf-preview-wrapper').removeClass('pdf-preview-wrapper-fw') + } + $('#pdf-preview-wrapper').append(`