[Enhancement] POS
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
index 6befedc..26519ac 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
@@ -17,6 +17,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Mode of Payment",
@@ -26,6 +27,7 @@
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -37,10 +39,37 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cash\nBank\nGeneral",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "accounts",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounts",
@@ -50,6 +79,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -68,7 +98,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:50.335559",
+ "modified": "2016-04-26 11:48:17.411796",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Mode of Payment",
@@ -115,6 +145,9 @@
"write": 0
}
],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payments/__init__.py b/erpnext/accounts/doctype/payments/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payments/__init__.py
diff --git a/erpnext/accounts/doctype/payments/payments.json b/erpnext/accounts/doctype/payments/payments.json
new file mode 100644
index 0000000..d52fb15
--- /dev/null
+++ b/erpnext/accounts/doctype/payments/payments.json
@@ -0,0 +1,191 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-05-08 23:49:38.842621",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Mode of Payment",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Mode of Payment",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "0.0",
+ "depends_on": "eval:parent.doctype == 'Sales Invoice'",
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "currency",
+ "permlevel": 0,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "type",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "mode_of_payment.type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "base_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Base Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-05-09 00:14:18.975568",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payments",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payments/payments.py b/erpnext/accounts/doctype/payments/payments.py
new file mode 100644
index 0000000..15cf0b2
--- /dev/null
+++ b/erpnext/accounts/doctype/payments/payments.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class Payments(Document):
+ pass
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 6d395af..8f25ba9 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -3,6 +3,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
+ "beta": 0,
"creation": "2013-05-24 12:15:51",
"custom": 0,
"docstatus": 0,
@@ -16,6 +17,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Applicable for User",
@@ -26,6 +28,7 @@
"options": "User",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -41,6 +44,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Series",
@@ -51,6 +55,7 @@
"options": "[Select]",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -62,37 +67,13 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Warehouse",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"default": "1",
"description": "",
"fieldname": "update_stock",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Update Stock",
@@ -100,6 +81,60 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "update_stock",
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Warehouse",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "ignore_pricing_rule",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Ignore Pricing Rule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -115,6 +150,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -122,6 +158,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -137,6 +174,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer",
@@ -147,6 +185,7 @@
"options": "Customer",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -162,6 +201,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Company",
@@ -172,6 +212,7 @@
"options": "Company",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -187,6 +228,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
@@ -197,6 +239,7 @@
"options": "Currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -208,19 +251,70 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "mode_of_payment",
- "fieldtype": "Link",
+ "fieldname": "allow_partial_payment",
+ "fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Mode of Payment",
+ "label": "Allow Partial Payment",
"length": 0,
"no_copy": 0,
- "options": "Mode of Payment",
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "payments",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payments",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payments",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -236,6 +330,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -243,6 +338,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -258,6 +354,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Print Format",
@@ -267,6 +364,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -282,6 +380,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Letter Head",
@@ -292,6 +391,7 @@
"options": "Letter Head",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -307,6 +407,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Print Heading",
@@ -317,6 +418,7 @@
"options": "Print Heading",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -332,6 +434,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Terms and Conditions",
@@ -342,6 +445,7 @@
"options": "Terms and Conditions",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -357,6 +461,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -364,6 +469,33 @@
"oldfieldtype": "Column Break",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Customer Group",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Customer Group",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -380,6 +512,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Territory",
@@ -390,6 +523,7 @@
"options": "Territory",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -405,6 +539,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Price List",
@@ -415,6 +550,60 @@
"options": "Price List",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "apply_discount",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Apply Discount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "Grand Total",
+ "depends_on": "apply_discount",
+ "fieldname": "apply_discount_on",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Apply Discount On",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Grand Total\nNet Total",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -430,6 +619,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -437,6 +627,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -448,10 +639,12 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "depends_on": "",
"fieldname": "write_off_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Account",
@@ -461,6 +654,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -476,6 +670,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Cost Center",
@@ -485,6 +680,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -500,6 +696,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Taxes and Charges",
@@ -510,6 +707,7 @@
"options": "Sales Taxes and Charges Template",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -525,6 +723,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -532,6 +731,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -547,6 +747,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Cash/Bank Account",
@@ -557,6 +758,7 @@
"options": "Account",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -572,6 +774,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Income Account",
@@ -582,6 +785,7 @@
"options": "Account",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -598,6 +802,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Expense Account",
@@ -606,6 +811,7 @@
"options": "Account",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -621,6 +827,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Cost Center",
@@ -631,6 +838,7 @@
"options": "Cost Center",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -649,7 +857,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:51.741253",
+ "modified": "2016-05-09 00:00:30.610878",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
@@ -696,9 +904,11 @@
"write": 0
}
],
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "user"
+ "title_field": "user",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 98a8509..634d63c 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -5,6 +5,7 @@
import frappe
from frappe import msgprint, _
from frappe.utils import cint
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import set_account_for_mode_of_payment
from frappe.model.document import Document
@@ -36,6 +37,9 @@
"company": self.company, "name": link_dn}):
frappe.throw(_("{0} does not belong to Company {1}").format(link_dn, self.company))
+ def before_save(self):
+ set_account_for_mode_of_payment(self)
+
def on_update(self):
self.set_defaults()
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index c534415..923514d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -129,7 +129,7 @@
paid_amount: function() {
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
- this.write_off_outstanding_amount();
+ this.write_off_amount();
this.frm.refresh_fields();
},
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index 62a9f52..0cf17da 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -2,50 +2,186 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe, json
+from frappe import _
+from frappe.utils import nowdate
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.stock.get_item_details import get_pos_profile
+from erpnext.controllers.accounts_controller import get_taxes_and_charges
@frappe.whitelist()
-def get_items(price_list, sales_or_purchase, item=None):
- condition = ""
- order_by = ""
- args = {"price_list": price_list}
+def get_pos_data():
+ doc = frappe.new_doc('Sales Invoice')
+ doc.update_stock = 1;
+ doc.is_pos = 1;
+ pos_profile = get_pos_profile(doc.company)
- if item:
- # search serial no
- item_code = frappe.db.sql("""select name as serial_no, item_code
- from `tabSerial No` where name=%s""", (item), as_dict=1)
- if item_code:
- item_code[0]["name"] = item_code[0]["item_code"]
- return item_code
+ if not pos_profile:
+ frappe.throw(_("Create pos profile first"))
- # search barcode
- item_code = frappe.db.sql("""select name, item_code from `tabItem`
- where barcode=%s""",
- (item), as_dict=1)
- if item_code:
- item_code[0]["barcode"] = item
- return item_code
+ pos_profile = frappe.get_doc('POS Profile', pos_profile.name)
+ update_pos_profile_data(doc, pos_profile)
+ update_multi_mode_option(doc, pos_profile)
- condition += " and ((CONCAT(i.name, i.item_name) like %(name)s) or (i.variant_of like %(name)s) or (i.item_group like %(name)s))"
- order_by = """if(locate(%(_name)s, i.name), locate(%(_name)s, i.name), 99999),
- if(locate(%(_name)s, i.item_name), locate(%(_name)s, i.item_name), 99999),
- if(locate(%(_name)s, i.variant_of), locate(%(_name)s, i.variant_of), 99999),
- if(locate(%(_name)s, i.item_group), locate(%(_name)s, i.item_group), 99999),"""
- args["name"] = "%%%s%%" % frappe.db.escape(item)
- args["_name"] = item.replace("%", "")
+ return {
+ 'doc': doc,
+ 'items': get_items(doc, pos_profile),
+ 'customers': get_customers(pos_profile),
+ 'pricing_rules': get_pricing_rules(doc),
+ 'mode_of_payment': get_mode_of_payment(doc),
+ 'print_template': frappe.db.get_value('Print Format', pos_profile.print_format, 'html') or '',
+ 'meta': {
+ 'invoice': frappe.get_meta('Sales Invoice'),
+ 'items': frappe.get_meta('Sales Invoice Item'),
+ 'taxes': frappe.get_meta('Sales Taxes and Charges')
+ }
+ }
- # locate function is used to sort by closest match from the beginning of the value
- return frappe.db.sql("""select i.name, i.item_name, i.image,
- item_det.price_list_rate, item_det.currency
- from `tabItem` i LEFT JOIN
- (select item_code, price_list_rate, currency from
- `tabItem Price` where price_list=%(price_list)s) item_det
- ON
- (item_det.item_code=i.name or item_det.item_code=i.variant_of)
- where
- i.has_variants = 0 and
- {condition}
- order by
- {order_by}
- i.name
- limit 24""".format(condition=condition, order_by=order_by), args, as_dict=1)
+def update_pos_profile_data(doc, pos_profile):
+ company_data = frappe.db.get_value('Company', doc.company, '*', as_dict=1)
+
+ doc.taxes_and_charges = pos_profile.taxes_and_charges
+ if doc.taxes_and_charges:
+ update_tax_table(doc)
+
+ doc.currency = pos_profile.currency or company_data.default_currency
+ doc.conversion_rate = 1.0
+ if doc.currency != company_data.default_currency:
+ doc.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency)
+ doc.selling_price_list = pos_profile.selling_price_list or frappe.db.get_value('Selling Settings', None, 'selling_price_list')
+ doc.naming_series = pos_profile.naming_series or 'SINV-'
+ doc.letter_head = pos_profile.letter_head or company_data.default_letter_head
+ doc.ignore_pricing_rule = pos_profile.ignore_pricing_rule
+ doc.apply_discount_on = pos_profile.apply_discount_on
+ doc.customer_group = pos_profile.customer_group or get_root('Customer Group')
+ doc.territory = pos_profile.territory or get_root('Territory')
+
+def get_root(table):
+ root = frappe.db.sql(""" select name from `tab%(table)s` having
+ min(lft)"""%{'table': table}, as_dict=1)
+
+ return root[0].name
+
+def update_multi_mode_option(doc, pos_profile):
+ from frappe.model import default_fields
+
+ for payment_mode in pos_profile.payments:
+ payment_mode = payment_mode.as_dict()
+
+ for fieldname in default_fields:
+ if fieldname in payment_mode:
+ del payment_mode[fieldname]
+
+ doc.append('payments', payment_mode)
+
+def update_tax_table(doc):
+ taxes = get_taxes_and_charges('Sales Taxes and Charges Template', doc.taxes_and_charges)
+ for tax in taxes:
+ doc.append('taxes', tax)
+
+def get_items(doc, pos_profile):
+ item_list = []
+ for item in frappe.get_all("Item", fields=["*"], filters={'disabled': 0, 'has_variants': 0}):
+ item_doc = frappe.get_doc('Item', item.name)
+ if item_doc.taxes:
+ item.taxes = json.dumps(dict(([d.tax_type, d.tax_rate] for d in
+ item_doc.get("taxes"))))
+
+ item.price_list_rate = frappe.db.get_value('Item Price', {'item_code': item.name,
+ 'price_list': doc.selling_price_list}, 'price_list_rate') or 0
+ item.default_warehouse = pos_profile.warehouse or item.default_warehouse or None
+ item.expense_account = pos_profile.expense_account or item.expense_account
+ item.income_account = pos_profile.income_account or item_doc.income_account
+ item.cost_center = pos_profile.cost_center or item_doc.selling_cost_center
+ item.actual_qty = frappe.db.get_value('Bin', {'item_code': item.name,
+ 'warehouse': item.default_warehouse}, 'actual_qty') or 0
+ item.serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where warehouse= %(warehouse)s
+ and item_code = %(item_code)s""", {'warehouse': item.default_warehouse, 'item_code': item.item_code})
+ item_list.append(item)
+
+ return item_list
+
+def get_customers(pos_profile):
+ filters = {'disabled': 0}
+ if pos_profile.customer:
+ filters.update({'name': pos_profile.customer})
+
+ return frappe.get_all("Customer", fields=["*"], filters = filters)
+
+def get_pricing_rules(doc):
+ if doc.ignore_pricing_rule == 0:
+ return frappe.db.sql(""" Select * from `tabPricing Rule` where docstatus < 2 and disable = 0
+ and selling = 1 and ifnull(company, '') in (%(company)s, '') and
+ ifnull(for_price_list, '') in (%(price_list)s, '') and %(date)s between
+ ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31') order by priority desc, name desc""",
+ {'company': doc.company, 'price_list': doc.selling_price_list, 'date': nowdate()}, as_dict=1)
+
+def get_mode_of_payment(doc):
+ return frappe.get_all('Mode of Payment Account', fields = ['distinct parent'], filters={'company': doc.company})
+
+@frappe.whitelist()
+def make_invoice(doc_list):
+ if isinstance(doc_list, basestring):
+ doc_list = json.loads(doc_list)
+
+ name_list = []
+
+ for docs in doc_list:
+ for name, doc in docs.items():
+ validate_customer(doc)
+ validate_item(doc)
+ si_doc = frappe.new_doc('Sales Invoice')
+ si_doc.offline_pos_name = name
+ si_doc.update(doc)
+ submit_invoice(si_doc)
+ name_list.append(name)
+
+ return name_list
+
+def validate_customer(doc):
+ if not frappe.db.get_value('Customer', doc.get('customer')):
+ customer_doc = frappe.new_doc('Customer')
+ customer_doc.customer_name = doc.get('customer')
+ customer_doc.customer_type = 'Company'
+ customer_doc.customer_group = doc.get('customer_group')
+ customer_doc.territory = doc.get('territory')
+ customer_doc.save(ignore_permissions = True)
+ frappe.db.commit()
+ doc['customer'] = customer_doc.name
+
+ return doc
+
+def validate_item(doc):
+ for item in doc.get('items'):
+ if not frappe.db.exists('Item', item.get('item_code')):
+ item_doc = frappe.new_doc('Item')
+ item_doc.name = item.get('item_code')
+ item_doc.item_code = item.get('item_code')
+ item_doc.item_name = item.get('item_name')
+ item_doc.description = item.get('description')
+ item_doc.default_warehouse = item.get('warehouse')
+ item_doc.stock_uom = item.get('stock_uom')
+ item_doc.item_group = item.get('item_group')
+ item_doc.save(ignore_permissions=True)
+ frappe.db.commit()
+
+def submit_invoice(si_doc):
+ try:
+ si_doc.insert()
+ si_doc.submit()
+ except Exception, e:
+ if frappe.message_log: frappe.message_log.pop()
+ frappe.db.rollback()
+ save_invoice(e, si_doc)
+
+def save_invoice(e, si_doc):
+ si_doc.docstatus = 0
+ si_doc.name = ''
+ si_doc.save(ignore_permissions=True)
+ make_scheduler_log(e, si_doc.name)
+
+def make_scheduler_log(e, sales_invoice):
+ scheduler_log = frappe.new_doc('Scheduler Log')
+ scheduler_log.error = e
+ scheduler_log.sales_invoice = sales_invoice
+ scheduler_log.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index c3c91a0..45c918c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -17,17 +17,6 @@
this.frm.set_df_property("debit_to", "print_hide", 0);
}
- // toggle to pos view if is_pos is 1 in user_defaults
- if ((is_null(this.frm.doc.is_pos) && cint(frappe.defaults.get_user_default("is_pos"))===1) || this.frm.doc.is_pos) {
- if(this.frm.doc.__islocal && !this.frm.doc.amended_from && !this.frm.doc.customer) {
- this.frm.set_value("is_pos", 1);
- this.is_pos(function() {
- if (cint(frappe.defaults.get_user_defaults("fs_pos_view"))===1)
- erpnext.pos.toggle(me.frm, true);
- });
- }
- }
-
erpnext.queries.setup_queries(this.frm, "Warehouse", function() {
return erpnext.queries.warehouse(me.frm.doc);
});
@@ -149,39 +138,6 @@
this.get_terms();
},
- is_pos: function(doc, dt, dn, callback_fn) {
- cur_frm.cscript.hide_fields(this.frm.doc);
- if(cur_frm.doc.__missing_values_set) return;
- if(cint(this.frm.doc.is_pos)) {
- if(!this.frm.doc.company) {
- this.frm.set_value("is_pos", 0);
- msgprint(__("Please specify Company to proceed"));
- } else {
- var me = this;
- return this.frm.call({
- doc: me.frm.doc,
- method: "set_missing_values",
- callback: function(r) {
- if(!r.exc) {
- if(r.message && r.message.print_format) {
- cur_frm.pos_print_format = r.message.print_format;
- }
- cur_frm.doc.__missing_values_set = true;
- me.frm.script_manager.trigger("update_stock");
- frappe.model.set_default_values(me.frm.doc);
- me.set_dynamic_labels();
- me.calculate_taxes_and_totals();
- if(callback_fn) callback_fn();
- frappe.after_ajax(function() {
- cur_frm.doc.__missing_values_set = false;
- })
- }
- }
- });
- }
- }
- },
-
customer: function() {
var me = this;
if(this.frm.updating_party_details) return;
@@ -245,11 +201,6 @@
this.write_off_outstanding_amount_automatically();
},
- paid_amount: function() {
- this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
- this.write_off_outstanding_amount_automatically();
- },
-
items_add: function(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "cost_center"]);
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 6678583..9da4aae 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -3,6 +3,7 @@
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
+ "beta": 0,
"creation": "2013-05-24 19:29:05",
"custom": 0,
"default_print_format": "Standard",
@@ -198,6 +199,31 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "offline_pos_name",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Offline POS Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@@ -656,6 +682,32 @@
{
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 0,
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Territory",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Territory",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 1,
"depends_on": "customer",
"fieldname": "currency_and_price_list",
@@ -1941,7 +1993,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
- "collapsible_depends_on": "paid_amount",
+ "collapsible_depends_on": "",
"depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)",
"fieldname": "payments_section",
"fieldtype": "Section Break",
@@ -1971,7 +2023,7 @@
"depends_on": "is_pos",
"fieldname": "cash_bank_account",
"fieldtype": "Link",
- "hidden": 0,
+ "hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
@@ -1996,9 +2048,34 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "depends_on": "is_pos",
- "fieldname": "column_break3",
- "fieldtype": "Column Break",
+ "fieldname": "payments",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payments",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payments",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "section_break_84",
+ "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -2007,6 +2084,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2014,35 +2092,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "unique": 0,
- "width": "50%"
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "depends_on": "is_pos",
- "fieldname": "paid_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Paid Amount",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "paid_amount",
- "oldfieldtype": "Currency",
- "options": "currency",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
"unique": 0
},
{
@@ -2074,6 +2123,110 @@
{
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 0,
+ "fieldname": "base_change_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Base Change Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_86",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "is_pos",
+ "fieldname": "paid_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Paid Amount",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "paid_amount",
+ "oldfieldtype": "Currency",
+ "options": "currency",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "change_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Change Amount",
+ "length": 0,
+ "no_copy": 1,
+ "options": "currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 1,
"collapsible_depends_on": "write_off_amount",
"depends_on": "grand_total",
@@ -3443,7 +3596,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-04-14 13:01:01.748816",
+ "modified": "2016-05-09 15:03:33.236351",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@@ -3530,6 +3683,7 @@
"write": 0
}
],
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 1,
"search_fields": "posting_date, due_date, customer, base_grand_total, outstanding_amount",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3120e73..952cce6 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -64,8 +64,8 @@
self.validate_fixed_asset()
self.set_income_account_for_fixed_assets()
- if cint(self.is_pos):
- self.validate_pos()
+ # if cint(self.is_pos):
+ # self.validate_pos()
if cint(self.update_stock):
self.validate_dropship_item()
@@ -83,6 +83,18 @@
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.update_packing_list()
+ def before_save(self):
+ set_account_for_mode_of_payment(self)
+
+ def update_change_amount(self):
+ self.base_paid_amount = 0.0
+ if self.paid_amount:
+ self.base_paid_amount = flt(self.paid_amount * self.conversion_rate, self.precision("base_paid_amount"))
+ self.change_amount = self.base_change_amount = 0.0
+ if self.paid_amount > self.grand_total:
+ self.change_amount = flt(self.paid_amount - self.grand_total, self.precision("change_amount"))
+ self.base_change_amount = flt(self.change_amount * self.conversion_rate, self.precision("base_change_amount"))
+
def on_submit(self):
if not self.recurring_id:
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
@@ -588,8 +600,7 @@
gl_entries += super(SalesInvoice, self).get_gl_entries()
def make_pos_gl_entries(self, gl_entries):
- if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
- bank_account_currency = get_account_currency(self.cash_bank_account)
+ if cint(self.is_pos) and self.paid_amount:
# POS, make payment entries
gl_entries.append(
self.get_gl_dict({
@@ -597,22 +608,43 @@
"party_type": "Customer",
"party": self.customer,
"against": self.cash_bank_account,
- "credit": self.base_paid_amount,
- "credit_in_account_currency": self.base_paid_amount \
- if self.party_account_currency==self.company_currency else self.paid_amount,
+ "credit": flt(self.base_paid_amount - self.base_change_amount),
+ "credit_in_account_currency": flt(self.base_paid_amount - self.base_change_amount) \
+ if self.party_account_currency==self.company_currency else flt(self.paid_amount - self.change_amount),
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
}, self.party_account_currency)
)
- gl_entries.append(
- self.get_gl_dict({
- "account": self.cash_bank_account,
- "against": self.customer,
- "debit": self.base_paid_amount,
- "debit_in_account_currency": self.base_paid_amount \
- if bank_account_currency==self.company_currency else self.paid_amount
- }, bank_account_currency)
- )
+
+ cash_account = ''
+ for payment_mode in self.payments:
+ if payment_mode.type == 'Cash':
+ cash_account = payment_mode.account
+
+ if payment_mode.base_amount > 0:
+ payment_mode_account_currency = get_account_currency(payment_mode.account)
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": payment_mode.account,
+ "against": self.customer,
+ "debit": payment_mode.base_amount,
+ "debit_in_account_currency": payment_mode.base_amount \
+ if payment_mode_account_currency==self.company_currency else payment_mode.amount
+ }, payment_mode_account_currency)
+ )
+
+ if self.change_amount:
+ cash_account = cash_account or self.payments[0].account
+ cash_account_currency = get_account_currency(cash_account)
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": cash_account,
+ "against": self.customer,
+ "credit": self.base_change_amount,
+ "credit_in_account_currency": self.base_change_amount \
+ if payment_mode_account_currency==self.company_currency else self.change_amount
+ }, payment_mode_account_currency)
+ )
def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos
@@ -680,7 +712,7 @@
account = frappe.db.get_value("Mode of Payment Account",
{"parent": mode_of_payment, "company": company}, "default_account")
if not account:
- frappe.msgprint(_("Please set default Cash or Bank account in Mode of Payment {0}").format(mode_of_payment))
+ frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}").format(mode_of_payment))
return {
"account": account
}
@@ -737,4 +769,9 @@
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
- return make_return_doc("Sales Invoice", source_name, target_doc)
\ No newline at end of file
+ return make_return_doc("Sales Invoice", source_name, target_doc)
+
+def set_account_for_mode_of_payment(self):
+ for data in self.payments:
+ if not data.account:
+ data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
\ 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 bac24d8..7208c2a 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -441,17 +441,45 @@
self.make_pos_profile()
self._insert_purchase_receipt()
-
pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1
pos["update_stock"] = 1
- pos["cash_bank_account"] = "_Test Bank - _TC"
- pos["paid_amount"] = 600.0
+ pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
+ {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300}]
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()
+ self._insert_purchase_receipt()
+
+ pos = copy.deepcopy(test_records[1])
+ pos["is_pos"] = 1
+ 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}]
+ 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`
where voucher_type = 'Sales Invoice' and voucher_no = %s""",
@@ -467,7 +495,7 @@
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],
@@ -475,8 +503,9 @@
[pos["taxes"][1]["account_head"], 0.0, 50.0],
[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0],
- [si.debit_to, 0.0, 600.0],
- ["_Test Bank - _TC", 600.0, 0.0]
+ [si.debit_to, 0.0, si.paid_amount],
+ ["_Test Bank - _TC", 300.0, 0.0],
+ ["Cash - _TC", cash_amount, 0.0]
])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index df7757b..04dd580 100644
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -1,46 +1,843 @@
+frappe.provide("erpnext.pos");
+{% include "erpnext/public/js/controllers/taxes_and_totals.js" %}
+
frappe.pages['pos'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
- title: __('Start Point-of-Sale (POS)'),
+ title: 'Point of Sale',
single_column: true
});
-
- page.main.html(frappe.render_template("pos_page", {}));
-
- var pos_type = frappe.ui.form.make_control({
- parent: page.main.find(".select-type"),
- df: {
- fieldtype: "Select",
- options: [
- {label: __("Billing (Sales Invoice)"), value:"Sales Invoice"},
- {value:"Sales Order"},
- {value:"Delivery Note"},
- {value:"Quotation"},
- {value:"Purchase Order"},
- {value:"Purchase Receipt"},
- {value:"Purchase Invoice"},
- ],
- fieldname: "pos_type"
- },
- only_input: true
- });
-
- pos_type.refresh();
-
- pos_type.set_input("Sales Invoice");
-
- page.main.find(".btn-primary").on("click", function() {
- erpnext.open_as_pos = true;
- new_doc(pos_type.get_value());
- });
-
- $.ajax({
- url: "/api/resource/POS Profile",
- success: function(data) {
- if(!data.data.length) {
- page.main.find(".pos-setting-message").removeClass('hide');
- }
- }
- })
-
+
+ wrapper = $(wrapper).find('.page-content')
+ new erpnext.pos.PointOfSale(page, wrapper)
}
+
+erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
+ init: function(page, wrapper){
+ this.page = page;
+ this.wrapper = wrapper;
+ this.set_indicator();
+ this.onload();
+ this.make_menu_list();
+ this.set_interval_for_si_sync();
+ this.si_docs = this.get_doc_from_localstorage();
+ },
+
+ check_internet_connection: function(){
+ var me = this;
+ //Check Internet connection after every 30 seconds
+ setInterval(function(){
+ me.set_indicator();
+ }, 30000)
+ },
+
+ set_indicator: function(){
+ var me = this;
+ // navigator.onLine
+ this.page.set_indicator("Offline", "grey")
+ frappe.call({
+ method:"frappe.handler.ping",
+ callback: function(r){
+ if(r.message){
+ me.connection_status = true;
+ me.page.set_indicator("Online", "green")
+ }
+ }
+ })
+ },
+
+ onload: function(){
+ var me = this;
+
+ this.get_data_from_server(function(){
+ me.create_new();
+ });
+
+ this.check_internet_connection();
+ },
+
+ make_menu_list: function(){
+ var me = this;
+
+ this.page.add_menu_item(__("Unsync Records"), function(){
+ me.show_unsync_invoice_list()
+ });
+
+ this.page.add_menu_item(__("Sync Master Data"), function(){
+ me.get_data_from_server()
+ });
+
+ this.page.add_menu_item(__("New Sales Invoice"), function() {
+ me.create_new()
+ })
+ },
+
+ show_unsync_invoice_list: function(){
+ var me = this;
+ this.si_docs = this.get_doc_from_localstorage();
+
+ this.list_dialog = new frappe.ui.Dialog({
+ title: 'Invoice List'
+ });
+
+ this.list_dialog.show();
+ this.list_body = this.list_dialog.body;
+ $(this.list_body).append('<div class="row list-row list-row-head pos-invoice-list">\
+ <div class="col-xs-6">Customer</div>\
+ <div class="col-xs-3 text-right">Grand Total</div>\
+ <div class="col-xs-3 text-right">Status</div>\
+ </div>')
+
+ $.each(this.si_docs, function(index, data){
+ for(key in data) {
+ $(frappe.render_template("pos_invoice_list", {
+ name: key,
+ customer: data[key].customer,
+ grand_total: data[key].grand_total,
+ status: (data[key].docstatus == 1) ? 'Submitted' : 'Draft'
+ })).appendTo($(me.list_body));
+ }
+ })
+
+ $(this.list_body).find('.list-row').click(function() {
+ me.name = $(this).attr('invoice-name')
+ doc_data = me.get_invoice_doc(me.si_docs)
+ if(doc_data){
+ me.frm.doc = doc_data[0][me.name];
+ me.refresh();
+ me.disable_input_field();
+ me.list_dialog.hide();
+ }
+ })
+ },
+
+ get_invoice_doc: function(si_docs){
+ var me = this;
+ this.si_docs = this.get_doc_from_localstorage();
+
+ return $.grep(this.si_docs, function(data){
+ for(key in data){
+ return key == me.name
+ }
+ })
+ },
+
+ get_data_from_server: function(callback){
+ var me = this;
+ frappe.call({
+ method: "erpnext.accounts.doctype.sales_invoice.pos.get_pos_data",
+ freeze: true,
+ callback: function(r){
+ window.items = r.message.items;
+ window.customers = r.message.customers;
+ window.pricing_rules = r.message.pricing_rules;
+ window.meta = r.message.meta;
+ window.print_template = r.message.print_template;
+ localStorage.setItem('doc', JSON.stringify(r.message.doc));
+ if(callback){
+ callback();
+ }
+ }
+ })
+ },
+
+ create_new: function(){
+ this.frm = {}
+ var me = this;
+ this.name = '';
+ this.frm.doc = JSON.parse(localStorage.getItem('doc'))
+ this.load_data();
+ this.setup();
+ },
+
+ load_data: function(){
+ this.items = window.items;
+ this.customers = window.customers;
+ this.pricing_rules = window.pricing_rules;
+
+ $.each(window.meta, function(i, data){
+ frappe.meta.sync(data)
+ })
+
+ this.print_template = frappe.render_template("print_template",
+ {content: window.print_template, title:"POS"})
+ },
+
+ setup: function(){
+ this.wrapper.html(frappe.render_template("pos", this.frm.doc));
+ this.set_transaction_defaults("Customer");
+ this.make();
+ this.set_primary_action();
+ },
+
+ set_transaction_defaults: function(party) {
+ var me = this;
+ this.party = party;
+ this.price_list = (party == "Customer" ?
+ this.frm.doc.selling_price_list : this.frm.doc.buying_price_list);
+ this.price_list_field = (party == "Customer" ? "selling_price_list" : "buying_price_list");
+ this.sales_or_purchase = (party == "Customer" ? "Sales" : "Purchase");
+ },
+
+ make: function() {
+ this.make_search();
+ this.make_customer();
+ this.make_item_list();
+ this.make_discount_field()
+ },
+
+ make_search: function() {
+ var me = this;
+ this.search = frappe.ui.form.make_control({
+ df: {
+ "fieldtype": "Data",
+ "label": "Item",
+ "fieldname": "pos_item",
+ "placeholder": "Search Item"
+ },
+ parent: this.wrapper.find(".search-area"),
+ only_input: true,
+ });
+
+ this.search.make_input();
+ this.search.$input.on("keyup", function() {
+ setTimeout(function() {
+ me.items = me.get_items();
+ me.make_item_list();
+ }, 1000);
+ });
+
+ this.party_field = frappe.ui.form.make_control({
+ df: {
+ "fieldtype": "Data",
+ "options": this.party,
+ "label": this.party,
+ "fieldname": this.party.toLowerCase(),
+ "placeholder": this.party
+ },
+ parent: this.wrapper.find(".party-area"),
+ only_input: true,
+ });
+
+ this.party_field.make_input();
+ },
+
+ make_customer: function() {
+ var me = this;
+
+ if(this.customers.length == 1){
+ this.party_field.$input.val(this.customers[0].name);
+ this.frm.doc.customer = this.customers[0].name;
+ }
+
+ this.party_field.$input.autocomplete({
+ source: function (request, response) {
+ me.customer_data = me.get_customers(request.term)
+ response($.map(me.customer_data, function(data){
+ return {label: data.name, value: data.name,
+ customer_group: data.customer_group, territory: data.territory}
+ }))
+ },
+ change: function(event, ui){
+ if(ui.item){
+ me.frm.doc.customer = ui.item.label;
+ me.frm.doc.customer_name = ui.item.customer_name;
+ me.frm.doc.customer_group = ui.item.customer_group;
+ me.frm.doc.territory = ui.item.territory;
+ }else{
+ me.frm.doc.customer = me.party_field.$input.val();
+ }
+ me.refresh();
+ }
+ })
+ },
+
+ get_customers: function(key){
+ var me = this;
+ key = key.toLowerCase()
+ return $.grep(this.customers, function(data) {
+ if(data.name.toLowerCase().match(key) || data.customer_name.toLowerCase().match(key)
+ || data.customer_group.toLowerCase().match(key)){
+ return data
+ }
+ })
+ },
+
+ make_item_list: function() {
+ var me = this;
+ if(!this.price_list) {
+ msgprint(__("Price List not found or disabled"));
+ return;
+ }
+
+ me.item_timeout = null;
+
+ var $wrap = me.wrapper.find(".item-list");
+ me.wrapper.find(".item-list").empty();
+
+ if (this.items) {
+ $.each(this.items, function(index, obj) {
+ if(index < 16){
+ $(frappe.render_template("pos_item", {
+ item_code: obj.name,
+ item_price: format_currency(obj.price_list_rate, obj.currency),
+ item_name: obj.name===obj.item_name ? "" : obj.item_name,
+ item_image: obj.image ? "url('" + obj.image + "')" : null,
+ color: frappe.get_palette(obj.item_name),
+ abbr: frappe.get_abbr(obj.item_name)
+ })).tooltip().appendTo($wrap);
+ }
+ });
+ }
+
+ if(this.items.length == 1){
+ this.search.$input.val("");
+ this.add_to_cart();
+ }
+
+ // if form is local then allow this function
+ $(me.wrapper).find("div.pos-item").on("click", function() {
+ me.customer_validate();
+ if(me.frm.doc.docstatus==0) {
+ me.items = me.get_items($(this).attr("data-item-code"))
+ me.add_to_cart();
+ }
+ });
+ },
+
+ get_items: function(item_code){
+ // To search item as per the key enter
+
+ var me = this;
+ this.item_serial_no = {}
+
+ if(item_code){
+ return $.grep(window.items, function(item){
+ if(item.item_code == item_code ){
+ return true
+ }
+ })
+ }
+
+ key = this.search.$input.val().toLowerCase();
+
+ if(key){
+ return $.grep(window.items, function(item){
+ if( (item.item_code.toLowerCase().match(key)) ||
+ (item.item_name.toLowerCase().match(key)) || (item.item_group.toLowerCase().match(key)) ){
+ return true
+ }else if(item.barcode){
+ return item.barcode == me.search.$input.val()
+ } else if (in_list(item.serial_nos, me.search.$input.val())){
+ me.item_serial_no[item.item_code] = me.search.$input.val()
+ return true
+ }
+ })
+ }else{
+ return window.items;
+ }
+ },
+
+ update_qty: function() {
+ var me = this;
+
+ $(this.wrapper).find(".pos-item-qty").on("change", function(){
+ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
+ me.update_qty_against_item_code(item_code, $(this).val());
+ })
+
+ $(this.wrapper).find("[data-action='increase-qty']").on("click", function(){
+ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
+ var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1;
+ me.update_qty_against_item_code(item_code, qty);
+ })
+
+ $(this.wrapper).find("[data-action='decrease-qty']").on("click", function(){
+ var item_code = $(this).parents(".pos-bill-item").attr("data-item-code");
+ var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1;
+ me.update_qty_against_item_code(item_code, qty);
+ })
+ },
+
+ update_qty_against_item_code: function(item_code, qty){
+ var me = this;
+ if(qty < 0){
+ frappe.throw(__("Quantity must be positive"));
+ }
+
+ this.remove_item = []
+ $.each(this.frm.doc["items"] || [], function(i, d) {
+ if (d.item_code == item_code) {
+ d.qty = flt(qty);
+ d.amount = flt(d.rate) * flt(d.qty);
+ if(d.qty==0){
+ me.remove_item.push(d.idx)
+ }
+ }
+ });
+ this.remove_zero_qty_item();
+ this.refresh();
+ },
+
+ remove_zero_qty_item: function(){
+ var me = this;
+ idx = 0
+ this.items = []
+ idx = 0
+ $.each(this.frm.doc["items"] || [], function(i, d) {
+ if(!in_list(me.remove_item, d.idx)){
+ d.idx = idx;
+ me.items.push(d);
+ idx++;
+ }
+ });
+
+ this.frm.doc["items"] = this.items;
+ },
+
+ make_discount_field: function(){
+ var me = this;
+
+ this.wrapper.find('input.discount-percentage').on("change", function() {
+ me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage"));
+ total = me.frm.doc.grand_total
+
+ if(me.frm.doc.apply_discount_on == 'Net Total'){
+ total = me.frm.doc.net_total
+ }
+
+ me.frm.doc.discount_amount = flt(total*flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount"));
+ me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount)
+ me.refresh();
+ });
+
+ this.wrapper.find('input.discount-amount').on("change", function() {
+ me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount"));
+ me.frm.doc.additional_discount_percentage = 0.0;
+ me.wrapper.find('input.discount-percentage').val(0);
+ me.refresh();
+ });
+ },
+
+ customer_validate: function(){
+ var me = this;
+
+ if(!this.frm.doc.customer){
+ frappe.throw(__("Please select customer"))
+ }
+ },
+
+ add_to_cart: function() {
+ var me = this;
+ var caught = false;
+ var no_of_items = me.wrapper.find(".pos-bill-item").length;
+
+ this.validate_serial_no()
+ this.validate_warehouse();
+
+ if (no_of_items != 0) {
+ $.each(this.frm.doc["items"] || [], function(i, d) {
+ if (d.item_code == me.items[0].item_code) {
+ caught = true;
+ d.qty += 1;
+ d.amount = flt(d.rate) * flt(d.qty)
+ if(me.item_serial_no.length){
+ d.serial_no += '\n' + me.item_serial_no[d.item_code]
+ }
+ }
+ });
+ }
+
+ // if item not found then add new item
+ if (!caught)
+ this.add_new_item_to_grid();
+
+ this.refresh();
+ },
+
+ add_new_item_to_grid: function() {
+ var me = this;
+ this.child = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + " Item", "items");
+ this.child.item_code = this.items[0].item_code;
+ this.child.item_name = this.items[0].item_name;
+ this.child.stock_uom = this.items[0].stock_uom;
+ this.child.description = this.items[0].description;
+ this.child.qty = 1;
+ this.child.item_group = this.items[0].item_group;
+ this.child.cost_center = this.items[0].cost_center;
+ this.child.income_account = this.items[0].income_account;
+ this.child.warehouse = this.items[0].default_warehouse;
+ this.child.price_list_rate = flt(this.items[0].price_list_rate, 9) / flt(this.frm.doc.conversion_rate, 9);
+ this.child.rate = flt(this.items[0].price_list_rate, 9) / flt(this.frm.doc.conversion_rate, 9);
+ this.child.actual_qty = this.items[0].actual_qty;
+ this.child.amount = flt(this.child.qty) * flt(this.child.rate);
+ this.child.serial_no = this.item_serial_no[this.child.item_code];
+ },
+
+ refresh: function() {
+ var me = this;
+ this.refresh_fields();
+ this.update_qty();
+ this.set_primary_action();
+ },
+ refresh_fields: function() {
+ this.apply_pricing_rule();
+ this.discount_amount_applied = false;
+ this._calculate_taxes_and_totals();
+ this.calculate_discount_amount();
+ this.show_items_in_item_cart();
+ this.set_taxes();
+ this.calculate_outstanding_amount();
+ this.set_totals();
+ },
+
+ get_company_currency: function() {
+ return erpnext.get_currency(this.frm.doc.company);
+ },
+
+ show_item_wise_taxes: function(){
+ return null;
+ },
+
+ show_items_in_item_cart: function() {
+ var me = this;
+ var $items = this.wrapper.find(".items").empty();
+ me.frm.doc.net_total = 0.0
+ $.each(this.frm.doc.items|| [], function(i, d) {
+ $(frappe.render_template("pos_bill_item", {
+ item_code: d.item_code,
+ item_name: (d.item_name===d.item_code || !d.item_name) ? "" : ("<br>" + d.item_name),
+ qty: d.qty,
+ actual_qty: d.actual_qty,
+ projected_qty: d.projected_qty,
+ rate: format_currency(d.rate, me.frm.doc.currency),
+ amount: format_currency(d.amount, me.frm.doc.currency)
+ })).appendTo($items);
+ });
+
+ this.wrapper.find("input.pos-item-qty").on("focus", function() {
+ $(this).select();
+ });
+ },
+
+ set_taxes: function(){
+ var me = this;
+ me.frm.doc.total_taxes_and_charges = 0.0
+
+ var taxes = this.frm.doc.taxes || [];
+ $(this.wrapper)
+ .find(".tax-area").toggleClass("hide", (taxes && taxes.length) ? false : true)
+ .find(".tax-table").empty();
+
+ $.each(taxes, function(i, d) {
+ if (d.tax_amount && cint(d.included_in_print_rate) == 0) {
+ $(frappe.render_template("pos_tax_row", {
+ description: d.description,
+ tax_amount: format_currency(flt(d.tax_amount_after_discount_amount),
+ me.frm.doc.currency)
+ })).appendTo(me.wrapper.find(".tax-table"));
+ }
+ });
+ },
+
+ set_totals: function() {
+ var me = this;
+ this.wrapper.find(".net-total").text(format_currency(me.frm.doc.total, me.frm.doc.currency));
+ this.wrapper.find(".grand-total").text(format_currency(me.frm.doc.grand_total, me.frm.doc.currency));
+ },
+
+ set_primary_action: function() {
+ var me = this;
+
+ if (this.frm.doc.docstatus==0 && this.frm.doc.outstanding_amount > 0) {
+ this.page.set_primary_action(__("Pay"), function() {
+ me.validate()
+ me.create_invoice();
+ me.make_payment();
+ });
+ }else if(this.frm.doc.docstatus == 0){
+ this.page.set_primary_action(__("Submit"), function() {
+ frappe.confirm(__("Do you really want to submit the invoice?"), function () {
+ me.write_off_amount()
+ })
+ })
+ }else if(this.frm.doc.docstatus == 1){
+ this.page.set_primary_action(__("Print"), function() {
+ html = frappe.render(me.print_template, me.frm.doc)
+ frappe.require("/assets/js/print_format_v3.min.js", function() {
+ w = _p.preview(html);
+ setTimeout(function(){
+ w.print();
+ }, 1000)
+ });
+ })
+ }
+ },
+
+ write_off_amount: function(){
+ var me = this;
+ var value = 0.0;
+
+ if(this.frm.doc.outstanding_amount > 0){
+ dialog = new frappe.ui.Dialog({
+ title: 'Write Off Amount',
+ fields: [
+ {fieldtype: "Currency", fieldname: "write_off_amount", label: __("Amount"), reqd: 1},
+ ]
+ });
+
+ dialog.show();
+
+ dialog.fields_dict.write_off_amount.$input.change(function(){
+ value = dialog.get_values()
+ })
+
+ dialog.set_primary_action(__("Submit"), function(){
+ me.frm.doc.write_off_amount = value.write_off_amount;
+ me.calculate_outstanding_amount();
+ dialog.hide();
+ me.change_status();
+ })
+ }else{
+ me.change_status();
+ }
+ },
+
+ change_status: function(){
+ if(this.frm.doc.docstatus == 0){
+ this.frm.doc.docstatus = 1;
+ this.update_invoice();
+ this.disable_input_field();
+ }
+ },
+
+ disable_input_field: function(){
+ var pointer_events = 'inherit'
+ $(this.wrapper).find('input').attr("disabled", false);
+
+ if(this.frm.doc.docstatus == 1){
+ pointer_events = 'none'
+ $(this.wrapper).find('input').attr("disabled", true);
+ }
+
+ $(this.wrapper).find('.pos-bill-wrapper').css('pointer-events', pointer_events);
+ $(this.wrapper).find('.pos-items-section').css('pointer-events', pointer_events);
+ this.set_primary_action();
+ },
+
+ create_invoice: function(){
+ var me = this;
+ var invoice_data = {}
+ this.si_docs = this.get_doc_from_localstorage();
+ if(this.name){
+ this.update_invoice()
+ }else{
+ this.name = $.now();
+ invoice_data[this.name] = this.frm.doc
+ this.si_docs.push(invoice_data)
+ this.update_localstorage();
+ this.set_primary_action();
+ }
+ },
+
+ update_invoice: function(){
+ var me = this;
+ this.si_docs = this.get_doc_from_localstorage();
+ $.each(this.si_docs, function(index, data){
+ for(key in data){
+ if(key == me.name){
+ me.si_docs[index][key] = me.frm.doc
+ me.update_localstorage();
+ }
+ }
+ })
+ },
+
+ update_localstorage: function(){
+ try{
+ localStorage.setItem('sales_invoice_doc', JSON.stringify(this.si_docs));
+ }catch(e){
+ frappe.throw(__("LocalStorage is full , did not save"))
+ }
+ },
+
+ get_doc_from_localstorage: function(){
+ return JSON.parse(localStorage.getItem('sales_invoice_doc')) || [];
+ },
+
+ set_interval_for_si_sync: function(){
+ var me = this;
+ setInterval(function(){
+ me.sync_sales_invoice()
+ }, 6000)
+ },
+
+ sync_sales_invoice: function(){
+ var me = this;
+ this.si_docs = this.get_submitted_invoice()
+
+ if(this.connection_status && this.si_docs.length){
+ frappe.call({
+ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
+ args: {
+ doc_list: me.si_docs
+ },
+ callback: function(r){
+ if(r.message){
+ me.removed_items = r.message;
+ me.remove_doc_from_localstorage();
+ }
+ }
+ })
+ }
+ },
+
+ get_submitted_invoice: function(){
+ invoices = []
+ docs = this.get_doc_from_localstorage()
+ if(docs){
+ invoices = $.map(docs, function(data){
+ for(key in data){
+ if(data[key].docstatus == 1){
+ return data
+ }
+ }
+ });
+ }
+
+ return invoices
+ },
+
+ remove_doc_from_localstorage: function(){
+ var me = this;
+ this.si_docs = this.get_doc_from_localstorage();
+ if(this.removed_items){
+ $.each(this.si_docs, function(index, data){
+ for(key in data){
+ if(in_list(me.removed_items, key)){
+ me.si_docs.splice(index)
+ }
+ }
+ })
+ this.update_localstorage();
+ }
+ },
+
+ validate: function(){
+ var me = this;
+ this.customer_validate();
+ this.item_validate();
+ },
+
+ item_validate: function(){
+ if(this.frm.doc.items.length == 0){
+ frappe.throw(__("Select items to save the invoice"))
+ }
+ },
+
+ validate_serial_no: function(){
+ var me = this;
+ var item_code = serial_no = '';
+ for (key in this.item_serial_no){
+ item_code = key;
+ serial_no = me.item_serial_no[key]
+ }
+
+ if(item_code && serial_no){
+ $.each(this.frm.doc.items, function(index, data){
+ if(data.item_code == item_code){
+ if(in_list(data.serial_no.split('\n'), serial_no)){
+ frappe.throw(__(repl("Serial no %(serial_no)s is already taken", {
+ 'serial_no': serial_no
+ })))
+ }
+ }
+ })
+ }
+ },
+
+ apply_pricing_rule: function(){
+ var me = this;
+ $.each(this.frm.doc["items"], function(n, item) {
+ pricing_rule = me.get_pricing_rule(item)
+ me.validate_pricing_rule(pricing_rule)
+ if(pricing_rule.length){
+ item.margin_type = pricing_rule[0].margin_type;
+ item.price_list_rate = pricing_rule[0].price || item.price_list_rate;
+ item.margin_rate_or_amount = pricing_rule[0].margin_rate_or_amount;
+ item.discount_percentage = pricing_rule[0].discount_percentage || 0.0;
+ me.apply_pricing_rule_on_item(item)
+ }
+ })
+ },
+
+ get_pricing_rule: function(item){
+ var me = this;
+ return $.grep(this.pricing_rules, function(data){
+ if(data.item_code == item.item_code || in_list(['All Item Groups', item.item_group], data.item_group)) {
+ if(in_list(['Customer', 'Customer Group', 'Territory'], data.applicable_for)){
+ return me.validate_condition(data)
+ }else{
+ return true
+ }
+ }
+ })
+ },
+
+ validate_condition: function(data){
+ //This method check condition based on applicable for
+ condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for]
+ if(in_list(condition[1], condition[0])){
+ return true
+ }
+ },
+
+ get_mapper_for_pricing_rule: function(data){
+ return {
+ 'Customer': [data.customer, [this.doc.customer]],
+ 'Customer Group': [data.customer_group, [this.doc.customer_group, 'All Customer Groups']],
+ 'Territory': [data.territory, [this.doc.territory, 'All Territories']],
+ }
+ },
+
+ validate_pricing_rule: function(pricing_rule){
+ //This method validate duplicate pricing rule
+ var pricing_rule_name = '';
+ var priority = 0;
+ var pricing_rule_list = [];
+ var priority_list = []
+
+ if(pricing_rule.length > 1){
+
+ $.each(pricing_rule, function(index, data){
+ pricing_rule_name += data.name + ','
+ priority_list.push(data.priority)
+ if(priority <= data.priority){
+ priority = data.priority
+ pricing_rule_list.push(data)
+ }
+ })
+
+ count = 0
+ $.each(priority_list, function(index, value){
+ if(value == priority){
+ count++
+ }
+ })
+
+ if(priority == 0 || count > 1){
+ frappe.throw(__(repl("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: %(pricing_rule)s", {
+ 'pricing_rule': pricing_rule_name
+ })))
+ }
+
+ return pricing_rule_list
+ }
+ },
+
+ validate_warehouse: function(){
+ if(!this.items[0].default_warehouse){
+ frappe.throw(__("Deafault warehouse is required for selected item"))
+ }
+ }
+})
\ No newline at end of file
diff --git a/erpnext/accounts/party_status.py b/erpnext/accounts/party_status.py
index ff095b1..5a638e5 100644
--- a/erpnext/accounts/party_status.py
+++ b/erpnext/accounts/party_status.py
@@ -37,6 +37,7 @@
party = frappe.get_doc(party_type, name)
filters = get_filters_for(doc.doctype)
+ party.flags.ignore_mandatory = True
status = None
if filters:
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 204c236..2ede901 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -33,6 +33,7 @@
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
+
if not self.meta.get_field("is_return") or not self.is_return:
self.validate_value("base_grand_total", ">=", 0)
@@ -54,17 +55,19 @@
if not self.get("__islocal"):
validate_recurring_document(self)
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
-
- self.validate_paid_amount()
-
+
+ if self.doctype == 'Purchase Invoice':
+ self.validate_paid_amount()
+
def validate_paid_amount(self):
if hasattr(self, "is_pos") or hasattr(self, "is_paid"):
is_paid = self.get("is_pos") or self.get("is_paid")
if cint(is_paid) == 1:
- if flt(self.paid_amount) == 0:
+ if flt(self.paid_amount) == 0 and flt(self.outstanding_amount) > 0:
if self.cash_bank_account:
self.paid_amount = flt(flt(self.grand_total) - flt(self.write_off_amount),
self.precision("paid_amount"))
+ self.base_paid_amount = flt(self.paid_amount * self.conversion_rate, self.precision("base_paid_amount"))
else:
# show message that the amount is not paid
self.paid_amount = 0
@@ -72,9 +75,6 @@
else:
frappe.db.set(self,'paid_amount',0)
- frappe.db.set(self, 'base_paid_amount',
- flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
-
def on_update_after_submit(self):
if self.meta.get_field("is_recurring"):
validate_recurring_document(self)
@@ -510,7 +510,7 @@
elif asset.status in ("Scrapped", "Cancelled", "Sold"):
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
.format(d.idx, d.asset, asset.status))
-
+
@frappe.whitelist()
def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, "tax_rate")
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 7210d76..eb75dee 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -435,13 +435,39 @@
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice":
+ self.calculate_paid_amount()
self.doc.round_floats_in(self.doc, ["paid_amount"])
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
- self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
- self.doc.precision("outstanding_amount"))
+
+ self.doc.outstanding_amount = 0
+ if total_amount_to_pay > paid_amount:
+ self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
+ self.doc.precision("outstanding_amount"))
+ self.change_amount()
+
elif self.doc.doctype == "Purchase Invoice":
self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
+
+ def calculate_paid_amount(self):
+ paid_amount = base_paid_amount = 0.0
+ for payment in self.doc.get('payments'):
+ payment.base_amount = flt(payment.amount * self.doc.conversion_rate)
+ paid_amount += payment.amount
+ base_paid_amount += payment.base_amount
+
+ self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
+ self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
+
+ def change_amount(self):
+ change_amount = 0.0
+ if self.doc.paid_amount > self.doc.grand_total:
+ change_amount = flt(self.doc.paid_amount - self.doc.grand_total,
+ self.doc.precision("change_amount"))
+
+ self.doc.change_amount = change_amount;
+ self.doc.base_change_amount = flt(change_amount * self.doc.conversion_rate,
+ self.doc.precision("base_change_amount"))
def calculate_margin(self, item):
total_margin = 0.0
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 1831907..9a949f2 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -16,13 +16,18 @@
"public/js/templates/address_list.html",
"public/js/templates/contact_list.html",
"public/js/controllers/stock_controller.js",
+ "public/js/controllers/payments.js",
"public/js/controllers/taxes_and_totals.js",
"public/js/controllers/transaction.js",
"public/js/pos/pos.html",
"public/js/pos/pos_bill_item.html",
"public/js/pos/pos_item.html",
"public/js/pos/pos_tax_row.html",
+ "public/js/pos/pos_print.html",
"public/js/pos/pos.js",
+ "public/js/pos/pos_invoice_list.html",
+ "public/js/payment/pos_payment.html",
+ "public/js/payment/payment_details.html",
"public/js/templates/item_selector.html",
"public/js/utils/item_selector.js"
],
diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css
index 6777e1e..284260d 100644
--- a/erpnext/public/css/erpnext.css
+++ b/erpnext/public/css/erpnext.css
@@ -124,3 +124,49 @@
.dashboard-list-item:last-child {
border-bottom: none;
}
+
+.payment-toolbar {
+ margin-left: 35px;
+}
+
+.payment-mode {
+ cursor: pointer;
+ font-family: sans-serif;
+ font-size: 15px;
+}
+
+.pos-payment-row .col-xs-6 {
+ padding :10px;
+}
+
+.pos-payment-row {
+ border-bottom:1px solid #d1d8dd;
+ margin: 2px 0px 5px 0px;
+}
+
+.pos-payment-row:hover {
+ background-color: #FCFFDD;
+ cursor: pointer;
+}
+
+.pos-keyboard-key, .delete-btn {
+ border: 1px solid #d1d8dd;
+ height:85px;
+ width:85px;
+ margin:10px 10px;
+ font-size:24px;
+ font-weight:200;
+}
+
+.amount-label {
+ font-size: 16px;
+}
+
+.selected-payment-mode {
+ background-color: #FCFFDD;
+ cursor: pointer;
+}
+
+.pos-invoice-list {
+ padding: 15px 10px;
+}
diff --git a/erpnext/public/js/controllers/payments.js b/erpnext/public/js/controllers/payments.js
new file mode 100644
index 0000000..7ac2b7b
--- /dev/null
+++ b/erpnext/public/js/controllers/payments.js
@@ -0,0 +1,158 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+erpnext.payments = erpnext.stock.StockController.extend({
+ make_payment: function() {
+ var me = this;
+
+ this.dialog = new frappe.ui.Dialog({
+ title: 'Payment'
+ });
+
+ this.dialog.show();
+ this.$body = this.dialog.body;
+ this.dialog.$wrapper.find('.modal-dialog').css("width", "750px");
+ this.set_payment_primary_action();
+ this.make_keyboard();
+ },
+
+ set_payment_primary_action: function(){
+ var me = this;
+
+ this.dialog.set_primary_action(__("Submit"), function() {
+ frappe.confirm(__("Do you really want to submit the invoice?"), function () {
+ me.write_off_amount();
+ me.dialog.hide();
+ })
+ })
+ },
+
+ make_keyboard: function(){
+ var me = this;
+ $(this.$body).empty();
+ $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc))
+ this.show_payment_details();
+ this.bind_keyboard_event()
+ },
+
+ pay_amount: function(){
+ var me = this;
+ this.make_multimode_payment();
+ this.calculate_outstanding_amount()
+ this.show_payment_details();
+ },
+
+ make_multimode_payment: function(){
+ var me = this;
+
+ if(this.frm.doc.change_amount > 0){
+ me.payment_val = me.doc.outstanding_amount
+ }
+
+ this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments");
+ this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value();
+ this.payments.amount = flt(this.payment_val);
+ },
+
+ show_payment_details: function(){
+ var me = this;
+ multimode_payments = $(this.$body).find('.multimode-payments').empty();
+ if(this.frm.doc.payments.length){
+ $.each(this.frm.doc.payments, function(index, data){
+ $(frappe.render_template('payment_details', {
+ mode_of_payment: data.mode_of_payment,
+ amount: data.amount,
+ idx: data.idx,
+ currency: me.frm.doc.currency,
+ type: data.type
+ })).appendTo(multimode_payments)
+ })
+ }else{
+ $("<p>No payment mode selected in pos profile</p>").appendTo(multimode_payments)
+ }
+ },
+
+ bind_keyboard_event: function(){
+ var me = this;
+ this.payment_val = '';
+ this.bind_payment_mode_keys_event();
+ this.bind_keyboard_keys_event();
+ },
+
+ bind_payment_mode_keys_event: function(){
+ var me = this;
+ $(this.$body).find('.pos-payment-row').click(function(){
+ me.idx = $(this).attr("idx");
+ me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx}));
+ me.highlight_selected_row()
+ me.payment_val = 0.0
+ if(me.frm.doc.outstanding_amount > 0 && flt(me.selected_mode.val()) == 0.0){
+ //When user first time click on row
+ me.payment_val = flt(me.frm.doc.outstanding_amount)
+ me.selected_mode.val(format_number(me.payment_val, 2));
+ me.update_paid_amount()
+ me.bind_amount_change_event();
+ }else if(flt(me.selected_mode.val()) > 0){
+ //If user click on existing row which has value
+ me.payment_val = flt(me.selected_mode.val());
+ }
+ me.selected_mode.select()
+ })
+ },
+
+ highlight_selected_row: function(){
+ var me = this;
+ selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx}));
+ $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode')
+ selected_row.addClass('selected-payment-mode')
+ $(this.$body).find('.amount').attr('disabled', true);
+ this.selected_mode.attr('disabled', false);
+ },
+
+ bind_keyboard_keys_event: function(){
+ var me = this;
+ $(this.$body).find('.pos-keyboard-key').click(function(){
+ me.payment_val += $(this).text();
+ me.selected_mode.val(format_number(me.payment_val, 2))
+ me.idx = me.selected_mode.attr("idx")
+ me.update_paid_amount()
+ })
+
+ $(this.$body).find('.delete-btn').click(function(){
+ me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1);
+ me.selected_mode.val(format_number(me.payment_val, 2));
+ me.idx = me.selected_mode.attr("idx")
+ me.update_paid_amount();
+ })
+
+ },
+
+ bind_amount_change_event: function(){
+ var me = this;
+ me.selected_mode.change(function(){
+ me.payment_val = $(this).val() || 0.0;
+ me.selected_mode.val(format_number(me.payment_val, 2))
+ me.idx = me.selected_mode.attr("idx")
+ me.update_paid_amount()
+ })
+ },
+
+ update_paid_amount: function(){
+ var me = this;
+ $.each(this.frm.doc.payments, function(index, data){
+ if(cint(me.idx) == cint(data.idx)){
+ data.amount = flt(me.selected_mode.val(), 2)
+ }
+ })
+ this.calculate_outstanding_amount();
+ this.show_amounts();
+ },
+
+ show_amounts: function(){
+ var me = this;
+ $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency));
+ $(this.$body).find('.change_amount').text(format_currency(this.frm.doc.change_amount, this.frm.doc.currency))
+ $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, this.frm.doc.currency))
+ this.update_invoice();
+ }
+})
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 1a234a3..f775f29 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -1,13 +1,30 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
+erpnext.taxes_and_totals = erpnext.payments.extend({
+ apply_pricing_rule_on_item: function(item){
+ if(!item.margin_type){
+ item.margin_rate_or_amount = 0.0;
+ }
+
+ if(item.margin_type == "Percentage"){
+ item.total_margin = item.price_list_rate + item.price_list_rate * ( item.margin_rate_or_amount / 100);
+ }else{
+ item.total_margin = item.price_list_rate + item.margin_rate_or_amount;
+ }
+
+ item.rate = flt(item.total_margin , 2);
+
+ if(item.discount_percentage){
+ discount_value = flt(item.total_margin) * flt(item.discount_percentage) / 100;
+ item.rate = flt((item.total_margin) - (discount_value), precision('rate'));
+ }
+ },
+
calculate_taxes_and_totals: function(update_paid_amount) {
this.discount_amount_applied = false;
this._calculate_taxes_and_totals();
-
- if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount"))
- this.apply_discount_amount();
+ this.calculate_discount_amount();
// Advance calculation applicable to Sales /Purchase Invoice
if(in_list(["Sales Invoice", "Purchase Invoice"], this.frm.doc.doctype)
@@ -24,6 +41,11 @@
this.frm.refresh_fields();
},
+ calculate_discount_amount: function(){
+ if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount"))
+ this.apply_discount_amount();
+ },
+
_calculate_taxes_and_totals: function() {
this.validate_conversion_rate();
this.calculate_item_values();
@@ -98,7 +120,7 @@
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0 });
- if (!this.discount_amount_applied) {
+ if (!this.discount_amount_applied && cur_frm) {
cur_frm.cscript.validate_taxes_and_charges(tax.doctype, tax.name);
me.validate_inclusive_tax(tax);
}
@@ -433,13 +455,14 @@
apply_discount_amount: function() {
var me = this;
var distributed_amount = 0.0;
+ this.frm.doc.base_discount_amount = 0.0;
if (this.frm.doc.discount_amount) {
if(!this.frm.doc.apply_discount_on)
frappe.throw(__("Please select Apply Discount On"));
-
- this.frm.set_value("base_discount_amount",
- flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate, precision("base_discount_amount")))
+
+ this.frm.doc.base_discount_amount = flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate,
+ precision("base_discount_amount"));
var total_for_discount_amount = this.get_total_for_discount_amount();
// calculate item amount after Discount Amount
@@ -455,8 +478,6 @@
this.discount_amount_applied = true;
this._calculate_taxes_and_totals();
}
- } else {
- this.frm.set_value("base_discount_amount", 0);
}
},
@@ -524,18 +545,59 @@
this.frm.doc.paid_amount = 0
}
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
- this.frm.refresh_field("paid_amount");
- this.frm.refresh_field("base_paid_amount");
+
+ if(this.frm.refresh_field){
+ this.frm.refresh_field("paid_amount");
+ this.frm.refresh_field("base_paid_amount");
+ }
+
+ if(this.frm.doc.doctype == "Sales Invoice"){
+ this.calculate_paid_amount()
+ }
+
+ var outstanding_amount = 0.0
var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ?
this.frm.doc.paid_amount : this.frm.doc.base_paid_amount;
- var outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
- precision("outstanding_amount"));
+ if (total_amount_to_pay > paid_amount){
+ outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
+ precision("outstanding_amount"));
+ }
} else if(this.frm.doc.doctype == "Purchase Invoice") {
var outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount"));
}
- this.frm.set_value("outstanding_amount", outstanding_amount);
- }
+
+ this.frm.doc.outstanding_amount = outstanding_amount;
+ this.calculate_change_amount()
+ },
+
+ calculate_paid_amount: function(){
+ var me = this;
+ var paid_amount = base_paid_amount = 0.0;
+ $.each(this.frm.doc['payments'] || [], function(index, data){
+ if(data.amount > 0){
+ data.base_amount = flt(data.amount * me.frm.doc.conversion_rate);
+ paid_amount += data.amount;
+ base_paid_amount += data.base_amount;
+ }
+ })
+
+ this.frm.doc.paid_amount = flt(paid_amount, precision("paid_amount"));
+ this.frm.doc.base_paid_amount = flt(base_paid_amount, precision("base_paid_amount"));
+ },
+
+ calculate_change_amount: function(){
+ var change_amount = 0.0;
+ if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
+ change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total,
+ precision("change_amount"))
+ }
+
+ this.frm.doc.change_amount = flt(change_amount,
+ precision("change_amount"))
+ this.frm.doc.base_change_amount = flt(change_amount * this.frm.doc.conversion_rate,
+ precision("base_change_amount"))
+ },
})
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3f6ea5a..c622466 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -165,7 +165,7 @@
erpnext.hide_company();
this.show_item_wise_taxes();
this.set_dynamic_labels();
- erpnext.pos.make_pos_btn(this.frm);
+ // erpnext.pos.make_pos_btn(this.frm);
this.setup_sms();
this.make_show_payments_btn();
},
@@ -550,7 +550,7 @@
setup_field_label_map(["base_total", "base_net_total", "base_total_taxes_and_charges",
"base_discount_amount", "base_grand_total", "base_rounded_total", "base_in_words",
"base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "total_amount_to_pay",
- "base_paid_amount", "base_write_off_amount"
+ "base_paid_amount", "base_write_off_amount", "base_change_amount"
], company_currency);
setup_field_label_map(["total", "net_total", "total_taxes_and_charges", "discount_amount",
diff --git a/erpnext/public/js/payment/payment_details.html b/erpnext/public/js/payment/payment_details.html
new file mode 100644
index 0000000..b0f61d8
--- /dev/null
+++ b/erpnext/public/js/payment/payment_details.html
@@ -0,0 +1,4 @@
+<div class="row pos-payment-row" type="{{type}}" idx={{idx}}>
+ <div class="col-xs-6"><h5 class="payment-mode text-ellipsis" idx="{{idx}}"> {{mode_of_payment}} </h5></div>
+ <div class="col-xs-6 text-right"><input disabled data-fieldtype="Currency" class="input-with-feedback form-control text-right amount" idx="{{idx}}" type="text" value="{{format_number(amount, 2)}}"></div>
+</div>
\ No newline at end of file
diff --git a/erpnext/public/js/payment/pos_payment.html b/erpnext/public/js/payment/pos_payment.html
new file mode 100644
index 0000000..e93644d
--- /dev/null
+++ b/erpnext/public/js/payment/pos_payment.html
@@ -0,0 +1,40 @@
+<div class="pos_payment row">
+ <div class="col-sm-6">
+ <div class="row">
+ <div class="col-xs-6 text-center">
+ <p class="amount-label"> Total <h3>{%= format_currency(grand_total, currency) %} </h3></p>
+ </div>
+ <div class="col-xs-6 text-center">
+ <p class="amount-label"> Paid <h3 class="paid_amount">{%= format_currency(paid_amount, currency) %}</h3></p>
+ </div>
+ </div>
+ <hr>
+ <div class="multimode-payments">
+ </div>
+ </div>
+ <div class="col-sm-6">
+ <div class="row">
+ <div class="col-xs-6 text-center">
+ <p class="amount-label"> Outstanding <h3 class="outstanding_amount">{%= format_currency(outstanding_amount, currency) %} </h3></p>
+ </div>
+ <div class="col-xs-6 text-center">
+ <p class="amount-label"> Change <h3 class="change_amount">{%= format_currency(change_amount, currency) %}</h3></p>
+ </div>
+ </div>
+ <hr>
+ <div class="payment-toolbar">
+ {% for(var i=0; i<3; i++) { %}
+ <div class="row">
+ {% for(var j=i*3; j<(i+1)*3; j++) { %}
+ <button type="button" class="btn btn-default pos-keyboard-key">{{j+1}}</button>
+ {% } %}
+ </div>
+ {% } %}
+ <div class="row">
+ <button type="button" class="btn btn-default delete-btn"><span class="mega-octicon octicon-triangle-left"></span></button>
+ <button type="button" class="btn btn-default pos-keyboard-key">0</button>
+ <button type="button" class="btn btn-default pos-keyboard-key">.</button>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html
index 36ef7c9..9d0ab60 100644
--- a/erpnext/public/js/pos/pos.html
+++ b/erpnext/public/js/pos/pos.html
@@ -24,22 +24,24 @@
<div class="tax-table small"></div>
</div>
</div>
- <div class="row pos-bill-row discount-amount-area">
- <div class="col-xs-6"><h6 class="text-muted">{%= __("Discount") %}</h6></div>
- <div class="col-xs-3 discount-field-col">
- <div class="input-group input-group-sm">
- <span class="input-group-addon">%</span>
- <input type="text" class="form-control discount-percentage text-right">
+ {% if (apply_discount_on) { %}
+ <div class="row pos-bill-row discount-amount-area">
+ <div class="col-xs-6"><h6 class="text-muted">{%= __("Discount") %}</h6></div>
+ <div class="col-xs-3 discount-field-col">
+ <div class="input-group input-group-sm">
+ <span class="input-group-addon">%</span>
+ <input type="text" class="form-control discount-percentage text-right">
+ </div>
</div>
- </div>
- <div class="col-xs-3 discount-field-col">
- <div class="input-group input-group-sm">
- <span class="input-group-addon">{%= get_currency_symbol(currency) %}</span>
- <input type="text" class="form-control discount-amount text-right"
- placeholder="{%= 0.00 %}">
+ <div class="col-xs-3 discount-field-col">
+ <div class="input-group input-group-sm">
+ <span class="input-group-addon">{%= get_currency_symbol(currency) %}</span>
+ <input type="text" class="form-control discount-amount text-right"
+ placeholder="{%= 0.00 %}">
+ </div>
</div>
- </div>
- </div>
+ </div>
+ {% } %}
<div class="row pos-bill-row grand-total-area">
<div class="col-xs-6"><h6>{%= __("Grand Total") %}</h6></div>
<div class="col-xs-6"><h6 class="grand-total text-right"></h6></div>
@@ -51,11 +53,12 @@
<div class="row pos-item-area">
</div>
+ <span id="customer-results" style="color:#68a;"></span>
<div class="row pos-item-toolbar">
<div class="search-area col-xs-12"></div>
</div>
<div class="item-list-area">
- <div class="item-list"></div>
+ <div class="app-listing item-list"></ul>
</div>
</div>
</div>
diff --git a/erpnext/public/js/pos/pos_bill_item.html b/erpnext/public/js/pos/pos_bill_item.html
index c93c76c..fe521f6 100644
--- a/erpnext/public/js/pos/pos_bill_item.html
+++ b/erpnext/public/js/pos/pos_bill_item.html
@@ -9,8 +9,7 @@
</div>
{% if(actual_qty != null) { %}
<div style="margin-top: 5px;" class="text-muted small text-right">
- <span title="{%= __("In Stock") %}">{%= actual_qty || 0 %}<span>
- <span title="{%= __("Projected") %}">({%= projected_qty || 0 %})<span>
+ <span title="{%= __("In Stock") %}">{%= actual_qty || 0 %}<span>
</div>
{% } %}
</div>
diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html
new file mode 100644
index 0000000..463b3e7
--- /dev/null
+++ b/erpnext/public/js/pos/pos_invoice_list.html
@@ -0,0 +1,5 @@
+<div class="row list-row pos-invoice-list" invoice-name = "{{name}}">
+ <div class="col-xs-6">{%= customer %}</div>
+ <div class="col-xs-3 text-right">{%= grand_total %}</div>
+ <div class="col-xs-3 text-right">{%= status %}</div>
+</div>
diff --git a/erpnext/public/js/pos/pos_print.html b/erpnext/public/js/pos/pos_print.html
new file mode 100644
index 0000000..d31a885
--- /dev/null
+++ b/erpnext/public/js/pos/pos_print.html
@@ -0,0 +1,25 @@
+<style>
+ .print-format table, .print-format tr,
+ .print-format td, .print-format div, .print-format p {
+ font-family: Monospace;
+ line-height: 200%;
+ vertical-align: middle;
+ }
+ @media screen {
+ .print-format {
+ width: 4in;
+ padding: 0.25in;
+ min-height: 8in;
+ }
+ }
+</style>
+
+<p class="text-center">
+ {{ company }}<br>
+</p>
+<p>
+ {{currency}}
+</p>
+{% for item in items %}
+ <p> {{item.item_code}} </p>
+{% endfor %}
\ No newline at end of file
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 5906a4f..e613daf 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -122,7 +122,7 @@
// check if child doctype is Sales Order Item/Qutation Item and calculate the rate
if(in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item"]), cdt)
- this.calculate_revised_margin_and_rate(item, doc,cdt, cdn);
+ this.apply_pricing_rule_on_item(item);
else
item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0),
precision("rate", item));
@@ -320,7 +320,7 @@
margin_rate_or_amount: function(doc, cdt, cdn) {
// calculated the revised total margin and rate on margin rate changes
item = locals[cdt][cdn];
- this.calculate_revised_margin_and_rate(item)
+ this.apply_pricing_rule_on_item(item)
this.calculate_taxes_and_totals();
cur_frm.refresh_fields();
},
@@ -328,26 +328,9 @@
margin_type: function(doc, cdt, cdn){
// calculate the revised total margin and rate on margin type changes
item = locals[cdt][cdn];
- this.calculate_revised_margin_and_rate(item, doc,cdt, cdn)
+ this.apply_pricing_rule_on_item(item, doc,cdt, cdn)
this.calculate_taxes_and_totals();
cur_frm.refresh_fields();
- },
-
- calculate_revised_margin_and_rate: function(item){
- if(in_list(["Percentage", "Amount"], item.margin_type)){
- if(item.margin_type == "Percentage")
- item.total_margin = item.price_list_rate + item.price_list_rate * ( item.margin_rate_or_amount / 100);
- else
- item.total_margin = item.price_list_rate + item.margin_rate_or_amount;
- item.rate = flt(item.total_margin * (1 - item.discount_percentage / 100.0),
- precision("rate", item));
- }
- else{
- item.rate_or_amount = 0.0;
- item.total_margin = 0.0;
- item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0),
- precision("rate", item));
- }
}
});