refactor: POS Invoice merging and cancellation (#24351)
* feat: pos invoice merging with background jobs * fix: invoice threshold for queueing job * refactor: cancellation flow of point of sale * feat: tests for cancellation of pos closing Co-authored-by: Marica <maricadsouza221197@gmail.com>
This commit is contained in:
parent
ac9e6ff704
commit
675a8330a4
@ -3,6 +3,7 @@
|
||||
|
||||
frappe.ui.form.on('POS Closing Entry', {
|
||||
onload: function(frm) {
|
||||
frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
|
||||
frm.set_query("pos_profile", function(doc) {
|
||||
return {
|
||||
filters: { 'user': doc.user }
|
||||
|
@ -11,6 +11,7 @@
|
||||
"column_break_3",
|
||||
"posting_date",
|
||||
"pos_opening_entry",
|
||||
"status",
|
||||
"section_break_5",
|
||||
"company",
|
||||
"column_break_7",
|
||||
@ -184,11 +185,27 @@
|
||||
"label": "POS Opening Entry",
|
||||
"options": "POS Opening Entry",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": "Draft",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"label": "Status",
|
||||
"options": "Draft\nSubmitted\nQueued\nCancelled",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-29 15:03:22.226113",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "POS Invoice Merge Log",
|
||||
"link_fieldname": "pos_closing_entry"
|
||||
}
|
||||
],
|
||||
"modified": "2021-01-12 12:21:05.388650",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Closing Entry",
|
||||
|
@ -6,13 +6,12 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import getdate, get_datetime, flt
|
||||
from collections import defaultdict
|
||||
from frappe.utils import get_datetime, flt
|
||||
from erpnext.controllers.status_updater import StatusUpdater
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices, unconsolidate_pos_invoices
|
||||
|
||||
class POSClosingEntry(Document):
|
||||
class POSClosingEntry(StatusUpdater):
|
||||
def validate(self):
|
||||
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
||||
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
||||
@ -64,17 +63,22 @@ class POSClosingEntry(Document):
|
||||
|
||||
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
||||
|
||||
def on_submit(self):
|
||||
merge_pos_invoices(self.pos_transactions)
|
||||
opening_entry = frappe.get_doc("POS Opening Entry", self.pos_opening_entry)
|
||||
opening_entry.pos_closing_entry = self.name
|
||||
opening_entry.set_status()
|
||||
opening_entry.save()
|
||||
|
||||
def get_payment_reconciliation_details(self):
|
||||
currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||
return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
|
||||
{"data": self, "currency": currency})
|
||||
|
||||
def on_submit(self):
|
||||
consolidate_pos_invoices(closing_entry=self)
|
||||
|
||||
def on_cancel(self):
|
||||
unconsolidate_pos_invoices(closing_entry=self)
|
||||
|
||||
def update_opening_entry(self, for_cancel=False):
|
||||
opening_entry = frappe.get_doc("POS Opening Entry", self.pos_opening_entry)
|
||||
opening_entry.pos_closing_entry = self.name if not for_cancel else None
|
||||
opening_entry.set_status()
|
||||
opening_entry.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
|
@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
// render
|
||||
frappe.listview_settings['POS Closing Entry'] = {
|
||||
get_indicator: function(doc) {
|
||||
var status_color = {
|
||||
"Draft": "red",
|
||||
"Submitted": "blue",
|
||||
"Queued": "orange",
|
||||
"Cancelled": "red"
|
||||
|
||||
};
|
||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||
}
|
||||
};
|
@ -13,7 +13,6 @@ from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profi
|
||||
class TestPOSClosingEntry(unittest.TestCase):
|
||||
def test_pos_closing_entry(self):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
|
||||
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
|
||||
@ -45,6 +44,49 @@ class TestPOSClosingEntry(unittest.TestCase):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
def test_cancelling_of_pos_closing_entry(self):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
|
||||
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
|
||||
pos_inv1.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500
|
||||
})
|
||||
pos_inv1.submit()
|
||||
|
||||
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||
pos_inv2.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
||||
})
|
||||
pos_inv2.submit()
|
||||
|
||||
pcv_doc = make_closing_entry_from_opening(opening_entry)
|
||||
payment = pcv_doc.payment_reconciliation[0]
|
||||
|
||||
self.assertEqual(payment.mode_of_payment, 'Cash')
|
||||
|
||||
for d in pcv_doc.payment_reconciliation:
|
||||
if d.mode_of_payment == 'Cash':
|
||||
d.closing_amount = 6700
|
||||
|
||||
pcv_doc.submit()
|
||||
|
||||
pos_inv1.load_from_db()
|
||||
self.assertRaises(frappe.ValidationError, pos_inv1.cancel)
|
||||
|
||||
si_doc = frappe.get_doc("Sales Invoice", pos_inv1.consolidated_invoice)
|
||||
self.assertRaises(frappe.ValidationError, si_doc.cancel)
|
||||
|
||||
pcv_doc.load_from_db()
|
||||
pcv_doc.cancel()
|
||||
si_doc.load_from_db()
|
||||
pos_inv1.load_from_db()
|
||||
self.assertEqual(si_doc.docstatus, 2)
|
||||
self.assertEqual(pos_inv1.status, 'Paid')
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
def init_user_and_profile(**args):
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
|
@ -16,6 +16,7 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
|
||||
onload(doc) {
|
||||
this._super();
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
|
||||
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
|
||||
this.frm.script_manager.trigger("is_pos");
|
||||
this.frm.refresh_fields();
|
||||
|
@ -6,10 +6,9 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from frappe.utils import cint, flt, getdate, nowdate, get_link_to_form
|
||||
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
|
||||
@ -58,6 +57,22 @@ class POSInvoice(SalesInvoice):
|
||||
self.apply_loyalty_points()
|
||||
self.check_phone_payments()
|
||||
self.set_status(update=True)
|
||||
|
||||
def before_cancel(self):
|
||||
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
||||
pos_closing_entry = frappe.get_all(
|
||||
"POS Invoice Reference",
|
||||
ignore_permissions=True,
|
||||
filters={ 'pos_invoice': self.name },
|
||||
pluck="parent",
|
||||
limit=1
|
||||
)
|
||||
frappe.throw(
|
||||
_('You need to cancel POS Closing Entry {} to be able to cancel this document.').format(
|
||||
get_link_to_form("POS Closing Entry", pos_closing_entry[0])
|
||||
),
|
||||
title=_('Not Allowed')
|
||||
)
|
||||
|
||||
def on_cancel(self):
|
||||
# run on cancel method of selling controller
|
||||
|
@ -290,7 +290,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
|
||||
def test_merging_into_sales_invoice_with_discount(self):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
||||
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
@ -306,7 +306,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
})
|
||||
pos_inv2.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
@ -315,7 +315,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
|
||||
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
||||
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
@ -348,7 +348,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
})
|
||||
pos_inv2.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
@ -357,7 +357,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
|
||||
def test_merging_with_validate_selling_price(self):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
||||
|
||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
|
||||
@ -393,7 +393,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
})
|
||||
pos_inv2.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv2.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||
|
@ -7,6 +7,8 @@
|
||||
"field_order": [
|
||||
"posting_date",
|
||||
"customer",
|
||||
"column_break_3",
|
||||
"pos_closing_entry",
|
||||
"section_break_3",
|
||||
"pos_invoices",
|
||||
"references_section",
|
||||
@ -76,11 +78,22 @@
|
||||
"label": "Consolidated Credit Note",
|
||||
"options": "Sales Invoice",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "pos_closing_entry",
|
||||
"fieldtype": "Link",
|
||||
"label": "POS Closing Entry",
|
||||
"options": "POS Closing Entry"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-29 15:08:41.317100",
|
||||
"modified": "2020-12-01 11:53:57.267579",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice Merge Log",
|
||||
|
@ -7,8 +7,11 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_doc, map_child_doc
|
||||
from frappe.utils import flt, getdate, nowdate
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
from frappe.model.mapper import map_doc, map_child_doc
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
|
||||
from six import iteritems
|
||||
|
||||
@ -61,7 +64,13 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
||||
|
||||
self.update_pos_invoices(sales_invoice, credit_note)
|
||||
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
|
||||
|
||||
def on_cancel(self):
|
||||
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||
|
||||
self.update_pos_invoices(pos_invoice_docs)
|
||||
self.cancel_linked_invoices()
|
||||
|
||||
def process_merging_into_sales_invoice(self, data):
|
||||
sales_invoice = self.get_new_sales_invoice()
|
||||
@ -163,17 +172,21 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
return sales_invoice
|
||||
|
||||
def update_pos_invoices(self, sales_invoice, credit_note):
|
||||
for d in self.pos_invoices:
|
||||
doc = frappe.get_doc('POS Invoice', d.pos_invoice)
|
||||
if not doc.is_return:
|
||||
doc.update({'consolidated_invoice': sales_invoice})
|
||||
else:
|
||||
doc.update({'consolidated_invoice': credit_note})
|
||||
def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
|
||||
for doc in invoice_docs:
|
||||
doc.load_from_db()
|
||||
doc.update({ 'consolidated_invoice': None if self.docstatus==2 else (credit_note if doc.is_return else sales_invoice) })
|
||||
doc.set_status(update=True)
|
||||
doc.save()
|
||||
|
||||
def get_all_invoices():
|
||||
def cancel_linked_invoices(self):
|
||||
for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
|
||||
if not si_name: continue
|
||||
si = frappe.get_doc('Sales Invoice', si_name)
|
||||
si.flags.ignore_validate = True
|
||||
si.cancel()
|
||||
|
||||
def get_all_unconsolidated_invoices():
|
||||
filters = {
|
||||
'consolidated_invoice': [ 'in', [ '', None ]],
|
||||
'status': ['not in', ['Consolidated']],
|
||||
@ -184,7 +197,7 @@ def get_all_invoices():
|
||||
|
||||
return pos_invoices
|
||||
|
||||
def get_invoices_customer_map(pos_invoices):
|
||||
def get_invoice_customer_map(pos_invoices):
|
||||
# pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Custoemr 2' : [{}] }
|
||||
pos_invoice_customer_map = {}
|
||||
for invoice in pos_invoices:
|
||||
@ -194,20 +207,82 @@ def get_invoices_customer_map(pos_invoices):
|
||||
|
||||
return pos_invoice_customer_map
|
||||
|
||||
def merge_pos_invoices(pos_invoices=[]):
|
||||
if not pos_invoices:
|
||||
pos_invoices = get_all_invoices()
|
||||
|
||||
pos_invoice_map = get_invoices_customer_map(pos_invoices)
|
||||
create_merge_logs(pos_invoice_map)
|
||||
def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
|
||||
invoices = pos_invoices or closing_entry.get('pos_transactions') or get_all_unconsolidated_invoices()
|
||||
invoice_by_customer = get_invoice_customer_map(invoices)
|
||||
|
||||
def create_merge_logs(pos_invoice_customer_map):
|
||||
for customer, invoices in iteritems(pos_invoice_customer_map):
|
||||
if len(invoices) >= 5 and closing_entry:
|
||||
enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
|
||||
closing_entry.set_status(update=True, status='Queued')
|
||||
else:
|
||||
create_merge_logs(invoice_by_customer, closing_entry)
|
||||
|
||||
def unconsolidate_pos_invoices(closing_entry):
|
||||
merge_logs = frappe.get_all(
|
||||
'POS Invoice Merge Log',
|
||||
filters={ 'pos_closing_entry': closing_entry.name },
|
||||
pluck='name'
|
||||
)
|
||||
|
||||
if len(merge_logs) >= 5:
|
||||
enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
|
||||
closing_entry.set_status(update=True, status='Queued')
|
||||
else:
|
||||
cancel_merge_logs(merge_logs, closing_entry)
|
||||
|
||||
def create_merge_logs(invoice_by_customer, closing_entry={}):
|
||||
for customer, invoices in iteritems(invoice_by_customer):
|
||||
merge_log = frappe.new_doc('POS Invoice Merge Log')
|
||||
merge_log.posting_date = getdate(nowdate())
|
||||
merge_log.customer = customer
|
||||
merge_log.pos_closing_entry = closing_entry.get('name', None)
|
||||
|
||||
merge_log.set('pos_invoices', invoices)
|
||||
merge_log.save(ignore_permissions=True)
|
||||
merge_log.submit()
|
||||
|
||||
if closing_entry:
|
||||
closing_entry.set_status(update=True, status='Submitted')
|
||||
closing_entry.update_opening_entry()
|
||||
|
||||
def cancel_merge_logs(merge_logs, closing_entry={}):
|
||||
for log in merge_logs:
|
||||
merge_log = frappe.get_doc('POS Invoice Merge Log', log)
|
||||
merge_log.flags.ignore_permissions = True
|
||||
merge_log.cancel()
|
||||
|
||||
if closing_entry:
|
||||
closing_entry.set_status(update=True, status='Cancelled')
|
||||
closing_entry.update_opening_entry(for_cancel=True)
|
||||
|
||||
def enqueue_job(job, invoice_by_customer, closing_entry):
|
||||
check_scheduler_status()
|
||||
|
||||
job_name = closing_entry.get("name")
|
||||
if not job_already_enqueued(job_name):
|
||||
enqueue(
|
||||
job,
|
||||
queue="long",
|
||||
timeout=10000,
|
||||
event="processing_merge_logs",
|
||||
job_name=job_name,
|
||||
closing_entry=closing_entry,
|
||||
invoice_by_customer=invoice_by_customer,
|
||||
now=frappe.conf.developer_mode or frappe.flags.in_test
|
||||
)
|
||||
|
||||
if job == create_merge_logs:
|
||||
msg = _('POS Invoices will be consolidated in a background process')
|
||||
else:
|
||||
msg = _('POS Invoices will be unconsolidated in a background process')
|
||||
|
||||
frappe.msgprint(msg, alert=1)
|
||||
|
||||
def check_scheduler_status():
|
||||
if is_scheduler_inactive() and not frappe.flags.in_test:
|
||||
frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
|
||||
|
||||
def job_already_enqueued(job_name):
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
if job_name in enqueued_jobs:
|
||||
return True
|
@ -7,7 +7,7 @@ import frappe
|
||||
import unittest
|
||||
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
|
||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||
|
||||
class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
@ -34,7 +34,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
})
|
||||
pos_inv3.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
||||
@ -79,7 +79,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
pos_inv_cn.paid_amount = -300
|
||||
pos_inv_cn.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
consolidate_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
||||
|
@ -6,7 +6,6 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, get_link_to_form
|
||||
from frappe.model.document import Document
|
||||
from erpnext.controllers.status_updater import StatusUpdater
|
||||
|
||||
class POSOpeningEntry(StatusUpdater):
|
||||
|
@ -5,7 +5,7 @@
|
||||
frappe.listview_settings['POS Opening Entry'] = {
|
||||
get_indicator: function(doc) {
|
||||
var status_color = {
|
||||
"Draft": "grey",
|
||||
"Draft": "red",
|
||||
"Open": "orange",
|
||||
"Closed": "green",
|
||||
"Cancelled": "red"
|
||||
|
@ -20,6 +20,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
var me = this;
|
||||
this._super();
|
||||
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice'];
|
||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
// show debit_to in print format
|
||||
this.frm.set_df_property("debit_to", "print_hide", 0);
|
||||
|
@ -1987,8 +1987,15 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-25 22:57:32.555067",
|
||||
"links": [
|
||||
{
|
||||
"custom": 1,
|
||||
"group": "Reference",
|
||||
"link_doctype": "POS Invoice",
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2021-01-12 12:16:15.192520",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -233,7 +233,25 @@ class SalesInvoice(SellingController):
|
||||
if len(self.payments) == 0 and self.is_pos:
|
||||
frappe.throw(_("At least one mode of payment is required for POS invoice."))
|
||||
|
||||
def check_if_consolidated_invoice(self):
|
||||
# since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice
|
||||
if self.doctype == "Sales Invoice" and self.is_consolidated:
|
||||
invoice_or_credit_note = "consolidated_credit_note" if self.is_return else "consolidated_invoice"
|
||||
pos_closing_entry = frappe.get_all(
|
||||
"POS Invoice Merge Log",
|
||||
filters={ invoice_or_credit_note: self.name },
|
||||
pluck="pos_closing_entry"
|
||||
)
|
||||
if pos_closing_entry:
|
||||
msg = _("To cancel a {} you need to cancel the POS Closing Entry {}. ").format(
|
||||
frappe.bold("Consolidated Sales Invoice"),
|
||||
get_link_to_form("POS Closing Entry", pos_closing_entry[0])
|
||||
)
|
||||
frappe.throw(msg, title=_("Not Allowed"))
|
||||
|
||||
def before_cancel(self):
|
||||
self.check_if_consolidated_invoice()
|
||||
|
||||
super(SalesInvoice, self).before_cancel()
|
||||
self.update_time_sheet(None)
|
||||
|
||||
|
@ -93,6 +93,12 @@ status_map = {
|
||||
["Open", "eval:self.docstatus == 1 and not self.pos_closing_entry"],
|
||||
["Closed", "eval:self.docstatus == 1 and self.pos_closing_entry"],
|
||||
["Cancelled", "eval:self.docstatus == 2"],
|
||||
],
|
||||
"POS Closing Entry": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus == 1"],
|
||||
["Queued", "eval:self.status == 'Queued'"],
|
||||
["Cancelled", "eval:self.docstatus == 2"],
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -741,6 +741,7 @@ erpnext.patches.v13_0.update_member_email_address
|
||||
erpnext.patches.v13_0.update_custom_fields_for_shopify
|
||||
erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||
erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log
|
||||
erpnext.patches.v13_0.add_po_to_global_search
|
||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||
erpnext.patches.v13_0.create_uae_pos_invoice_fields
|
||||
|
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "POS Invoice Merge Log")
|
||||
frappe.reload_doc("accounts", "doctype", "POS Closing Entry")
|
||||
if frappe.db.count('POS Invoice Merge Log'):
|
||||
frappe.db.sql('''
|
||||
UPDATE
|
||||
`tabPOS Invoice Merge Log` log, `tabPOS Invoice Reference` log_ref
|
||||
SET
|
||||
log.pos_closing_entry = (
|
||||
SELECT clo_ref.parent FROM `tabPOS Invoice Reference` clo_ref
|
||||
WHERE clo_ref.pos_invoice = log_ref.pos_invoice
|
||||
AND clo_ref.parenttype = 'POS Closing Entry'
|
||||
)
|
||||
WHERE
|
||||
log_ref.parent = log.name
|
||||
''')
|
||||
|
||||
frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Submitted' where docstatus = 1''')
|
||||
frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Cancelled' where docstatus = 2''')
|
Loading…
Reference in New Issue
Block a user