diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index b90cd2308c1..9418372c097 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -92,8 +92,8 @@ class GLEntry(Document): "Cost Center", self.cost_center, "company") return self.cost_center_company[self.cost_center] - - if self.cost_center and _get_cost_center_company() != self.company: + + if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) def validate_party(self): @@ -181,8 +181,8 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga # Update outstanding amt on against voucher if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: - frappe.db.sql("update `tab%s` set outstanding_amount=%s where name=%s" % - (against_voucher_type, '%s', '%s'), (bal, against_voucher)) + ref_doc = frappe.get_doc(against_voucher_type, against_voucher) + ref_doc.db_set('outstanding_amount', bal) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index a774e54e2c0..0c8df0220f8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -9,55 +9,54 @@ from frappe.model.document import Document from frappe.utils import flt, get_url, nowdate from erpnext.accounts.party import get_party_account from erpnext.accounts.utils import get_account_currency -from erpnext.setup.utils import get_exchange_rate from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults -class PaymentRequest(Document): +class PaymentRequest(Document): def validate(self): self.validate_payment_gateway_account() self.validate_payment_request() self.validate_currency() def validate_payment_request(self): - if frappe.db.get_value("Payment Request", {"reference_name": self.reference_name, + if frappe.db.get_value("Payment Request", {"reference_name": self.reference_name, "name": ("!=", self.name), "status": ("not in", ["Initiated", "Paid"]), "docstatus": 1}, "name"): frappe.throw(_("Payment Request already exists {0}".format(self.reference_name))) - + def validate_currency(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"): frappe.throw(_("Transaction currency must be same as Payment Gateway currency")) - + def validate_payment_gateway_account(self): if not self.payment_gateway: frappe.throw(_("Payment Gateway Account is not configured")) - + def validate_payment_gateway(self): if self.payment_gateway == "PayPal": if not frappe.db.get_value("PayPal Settings", None, "api_username"): if not frappe.conf.paypal_username: frappe.throw(_("PayPal Settings missing")) - + def on_submit(self): send_mail = True self.make_communication_entry() ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) - + if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart": send_mail = False - + if send_mail: self.send_payment_request() self.send_email() - + def on_cancel(self): self.set_as_cancelled() - + def get_payment_url(self): """ This is blanck method to trigger hooks call from individual payment gateway app which will return respective payment gateway""" pass - + def make_invoice(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart": @@ -65,26 +64,26 @@ class PaymentRequest(Document): si = make_sales_invoice(self.reference_name, ignore_permissions=True) si = si.insert(ignore_permissions=True) si.submit() - + def send_payment_request(self): self.payment_url = get_url("/api/method/erpnext.accounts.doctype.payment_request.payment_request.generate_payment_request?name={0}".format(self.name)) if self.payment_url: - frappe.db.set_value(self.doctype, self.name, "payment_url", self.payment_url) - frappe.db.set_value(self.doctype, self.name, "status", "Initiated") - + self.db_set('payment_url', self.payment_url) + self.db_set('status', 'Initiated') + def set_as_paid(self): if frappe.session.user == "Guest": frappe.set_user("Administrator") - + payment_entry = self.create_payment_entry() self.make_invoice() - + return payment_entry - + def create_payment_entry(self): """create entry""" frappe.flags.ignore_account_permission = True - + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if self.reference_doctype == "Sales Invoice": @@ -93,22 +92,22 @@ class PaymentRequest(Document): party_account = ref_doc.credit_to else: party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company) - + party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account) - + bank_amount = self.grand_total if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency: party_amount = ref_doc.base_grand_total else: party_amount = self.grand_total - - payment_entry = get_payment_entry(self.reference_doctype, self.reference_name, + + payment_entry = get_payment_entry(self.reference_doctype, self.reference_name, party_amount=party_amount, bank_account=self.payment_account, bank_amount=bank_amount) - + payment_entry.update({ "reference_no": self.name, "reference_date": nowdate(), - "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype, + "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype, self.reference_name, self.name) }) @@ -123,32 +122,32 @@ class PaymentRequest(Document): payment_entry.submit() #set status as paid for Payment Request - frappe.db.set_value(self.doctype, self.name, "status", "Paid") - + self.db_set('status', 'Paid') + return payment_entry - + def send_email(self): """send email with payment link""" frappe.sendmail(recipients=self.email_to, sender=None, subject=self.subject, - message=self.get_message(), attachments=[frappe.attach_print(self.reference_doctype, + message=self.get_message(), attachments=[frappe.attach_print(self.reference_doctype, self.reference_name, file_name=self.reference_name, print_format=self.print_format)]) - + def get_message(self): """return message with payment gateway link""" - + context = { "doc": frappe.get_doc(self.reference_doctype, self.reference_name), "payment_url": self.payment_url } - + return frappe.render_template(self.message, context) - + def set_failed(self): pass - + def set_as_cancelled(self): - frappe.db.set_value(self.doctype, self.name, "status", "Cancelled") - + self.db_set("status", "Cancelled") + def make_communication_entry(self): """Make communication entry""" comm = frappe.get_doc({ @@ -160,28 +159,28 @@ class PaymentRequest(Document): "reference_name": self.reference_name }) comm.insert(ignore_permissions=True) - + def get_payment_success_url(self): return self.payment_success_url @frappe.whitelist(allow_guest=True) def make_payment_request(**args): """Make payment request""" - + args = frappe._dict(args) - + ref_doc = frappe.get_doc(args.dt, args.dn) - + gateway_account = get_gateway_details(args) - + grand_total = get_amount(ref_doc, args.dt) - - existing_payment_request = frappe.db.get_value("Payment Request", + + existing_payment_request = frappe.db.get_value("Payment Request", {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ["!=", 2]}) - + if existing_payment_request: pr = frappe.get_doc("Payment Request", existing_payment_request) - + else: pr = frappe.new_doc("Payment Request") pr.update({ @@ -196,67 +195,67 @@ def make_payment_request(**args): "reference_doctype": args.dt, "reference_name": args.dn }) - + if args.return_doc: return pr if args.submit_doc: pr.insert(ignore_permissions=True) pr.submit() - + if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart": generate_payment_request(pr.name) frappe.db.commit() - + if not args.cart: return pr - + return pr.as_dict() def get_amount(ref_doc, dt): """get amount based on doctype""" if dt == "Sales Order": grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) - + if dt == "Sales Invoice": if ref_doc.party_account_currency == ref_doc.currency: grand_total = flt(ref_doc.outstanding_amount) else: grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate - + if grand_total > 0 : return grand_total - + else: frappe.throw(_("Payment Entry is already created")) - + def get_gateway_details(args): """return gateway and payment account of default payment gateway""" if args.get("payment_gateway"): return get_payment_gateway_account(args.get("payment_gateway")) - + if args.cart: payment_gateway_account = frappe.get_doc("Shopping Cart Settings").payment_gateway_account return get_payment_gateway_account(payment_gateway_account) - + gateway_account = get_payment_gateway_account({"is_default": 1}) - + if not gateway_account: frappe.throw(_("Payment Gateway Account is not configured")) - + return gateway_account - + def get_payment_gateway_account(args): - return frappe.db.get_value("Payment Gateway Account", args, - ["name", "payment_gateway", "payment_account", "message"], + return frappe.db.get_value("Payment Gateway Account", args, + ["name", "payment_gateway", "payment_account", "message"], as_dict=1) @frappe.whitelist() def get_print_format_list(ref_doctype): print_format_list = ["Standard"] - - print_format_list.extend([p.name for p in frappe.get_all("Print Format", + + print_format_list.extend([p.name for p in frappe.get_all("Print Format", filters={"doc_type": ref_doctype})]) - + return { "print_format": print_format_list } @@ -264,8 +263,7 @@ def get_print_format_list(ref_doctype): @frappe.whitelist(allow_guest=True) def generate_payment_request(name): frappe.get_doc("Payment Request", name).run_method("get_payment_url") - + @frappe.whitelist(allow_guest=True) def resend_payment_email(docname): return frappe.get_doc("Payment Request", docname).send_email() - \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ec63b1f1d2f..e7eee5f1ea9 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -450,14 +450,14 @@ class TestSalesInvoice(unittest.TestCase): si = frappe.copy_doc(pos) si.insert() si.submit() - + self.assertEquals(si.paid_amount, 600.0) self.pos_gl_entry(si, pos, 300) - + def test_make_pos_invoice(self): from erpnext.accounts.doctype.sales_invoice.pos import make_invoice - + set_perpetual_inventory() self.make_pos_profile() @@ -468,17 +468,17 @@ class TestSalesInvoice(unittest.TestCase): pos["update_stock"] = 1 pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300}, {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}] - - invoice_data = [{'09052016142': pos}] + + invoice_data = [{'09052016142': pos}] si = make_invoice(invoice_data) self.assertEquals(si[0], '09052016142') - + sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1}) si = frappe.get_doc('Sales Invoice', sales_invoice[0].name) self.assertEquals(si.grand_total, 630.0) - + self.pos_gl_entry(si, pos, 330) - + def pos_gl_entry(self, si, pos, cash_amount): # check stock ledger entries sle = frappe.db.sql("""select * from `tabStock Ledger Entry` @@ -495,7 +495,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(gl_entries) stock_in_hand = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"}) - + expected_gl_entries = sorted([ [si.debit_to, 630.0, 0.0], [pos["items"][0]["income_account"], 0.0, 500.0], @@ -952,6 +952,40 @@ class TestSalesInvoice(unittest.TestCase): self.assertNotEquals(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate)) + def test_party_status(self): + from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + from frappe.utils import random_string + + customer_name = 'test customer for status' + + if frappe.db.exists('Customer', customer_name): + customer = frappe.get_doc('Customer', customer_name) + customer.db_set('status', 'Active') + else: + customer = frappe.get_doc({ + 'doctype': 'Customer', + 'customer_name': customer_name, + 'customer_group': 'Commercial', + 'customer_type': 'Individual', + 'territory': 'Rest of the World' + }).insert() + + self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Active') + + invoice = create_sales_invoice(customer="test customer for status", + debit_to="_Test Receivable - _TC", + currency="USD", conversion_rate=50) + + self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Open') + + pe = get_payment_entry(invoice.doctype, invoice.name) + pe.reference_no = random_string(10) + pe.reference_date = invoice.posting_date + pe.insert() + pe.submit() + + self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Active') + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) diff --git a/erpnext/accounts/party_status.py b/erpnext/accounts/party_status.py index 5a638e571b7..68b48184e79 100644 --- a/erpnext/accounts/party_status.py +++ b/erpnext/accounts/party_status.py @@ -19,7 +19,7 @@ default_status = { 'Supplier': None } -def notify_status(doc, method): +def notify_status(doc, method=None): '''Notify status to customer, supplier''' party_type = None @@ -59,6 +59,7 @@ def notify_status(doc, method): update_status(party) party.update_modified() + party.notify_update() def get_party_status(doc): '''return party status based on open documents''' diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 6ae5bbea81a..2c25f5cd743 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -6,6 +6,7 @@ import frappe from frappe.utils import flt, comma_or from frappe import _ from frappe.model.document import Document +from erpnext.accounts.party_status import notify_status def validate_status(status, options): if status not in options: @@ -106,8 +107,7 @@ class StatusUpdater(Document): self.add_comment("Label", _(self.status)) if update: - frappe.db.set_value(self.doctype, self.name, "status", self.status, - update_modified=update_modified) + self.db_set('status', self.status, update_modified = update_modified) def validate_qty(self): """Validates qty at row level""" @@ -263,6 +263,7 @@ class StatusUpdater(Document): target = frappe.get_doc(args["target_parent_dt"], args["name"]) target.set_status(update=True) target.notify_update() + notify_status(target) def _update_modified(self, args, update_modified): args['update_modified'] = '' @@ -296,14 +297,16 @@ class StatusUpdater(Document): per_billed = ((ref_doc_qty if billed_qty > ref_doc_qty else billed_qty)\ / ref_doc_qty)*100 - frappe.db.set_value(ref_dt, ref_dn, "per_billed", per_billed) + + ref_doc = frappe.get_doc(ref_dt, ref_dn) + + ref_doc.db_set("per_billed", per_billed) if frappe.get_meta(ref_dt).get_field("billing_status"): if per_billed < 0.001: billing_status = "Not Billed" elif per_billed >= 99.99: billing_status = "Fully Billed" else: billing_status = "Partly Billed" - - frappe.db.set_value(ref_dt, ref_dn, "billing_status", billing_status) + ref_doc.db_set('billing_status', billing_status) def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None): """ diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4a2b9a9dae9..e4a1aabef64 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -23,7 +23,7 @@ class WarehouseRequired(frappe.ValidationError): pass class SalesOrder(SellingController): def __init__(self, arg1, arg2=None): super(SalesOrder, self).__init__(arg1, arg2) - + self.prev_link_mapper = { "Quotation": { "fieldname": "prevdoc_docname", @@ -34,7 +34,7 @@ class SalesOrder(SellingController): ] } } - + def validate(self): super(SalesOrder, self).validate() @@ -298,8 +298,8 @@ class SalesOrder(SellingController): delivered_qty += item.delivered_qty tot_qty += item.qty - frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100, - update_modified=False) + self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100, + update_modified=False) def set_indicator(self): """Set indicator for portal""" @@ -319,7 +319,7 @@ class SalesOrder(SellingController): mcount = month_map[reference_doc.recurring_type] self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount, cint(reference_doc.repeat_on_day_of_month))) - + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context)