Merge pull request #16629 from Anurag810/gross-and-net-profit-report
feat: Gross and Net Profit Report
diff --git a/.travis.yml b/.travis.yml
index 14260e3..869fe95 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,7 @@
- sudo rm /etc/apt/sources.list.d/docker.list
- sudo apt-get install hhvm && rm -rf /home/travis/.kiex/
- sudo apt-get purge -y mysql-common mysql-server mysql-client
- - nvm install v7.10.0
+ - nvm install 10
- pip install python-coveralls
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py
- sudo python install.py --develop --user travis --without-bench-setup
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 89ac6d9..d0ae9c4 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '11.1.4'
+__version__ = '11.1.13'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 9ff8b04..e638fc7 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -60,7 +60,7 @@
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
last_gl_entry, skip = False, False
- booking_end_date = getdate(add_days(today(), -1)) if not end_date else end_date
+ booking_end_date = getdate(add_days(today(), -1) if not end_date else end_date)
if booking_end_date < item.service_start_date or \
(item.service_stop_date and booking_end_date.month > item.service_stop_date.month):
return None, None, None, True
@@ -71,7 +71,7 @@
last_gl_entry = True
booking_end_date = item.service_stop_date
- booking_start_date = getdate(add_months(today(), -1)) if not start_date else start_date
+ booking_start_date = getdate(add_months(today(), -1) if not start_date else start_date)
booking_start_date = booking_start_date \
if booking_start_date > item.service_start_date else item.service_start_date
@@ -113,7 +113,6 @@
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
-
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
@@ -128,15 +127,19 @@
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
# start_date: 1st of the last month or the start date
# end_date: end_date or today-1
+ enable_check = "enable_deferred_revenue" \
+ if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
gl_entries = []
for item in doc.get('items'):
+ if not item.get(enable_check): continue
+
skip = False
last_gl_entry, booking_start_date, booking_end_date, skip = \
get_booking_dates(doc, item, start_date, end_date)
if skip: continue
- total_days = date_diff(item.service_end_date, item.service_start_date)
+ total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
account_currency = get_account_currency(item.expense_account)
@@ -175,6 +178,10 @@
'project': project
}, account_currency)
)
-
if gl_entries:
- make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
+ try:
+ make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
+ frappe.db.commit()
+ except:
+ frappe.db.rollback()
+ frappe.log_error(message = frappe.get_traceback(), title = _("Error while processing deferred accounting for {0}").format(doc.name))
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json
index 57a9c7a..14e9070 100644
--- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json
+++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -15,6 +16,7 @@
"fields": [
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -32,7 +34,7 @@
"label": "Series",
"length": 0,
"no_copy": 0,
- "options": "Cashier-closing-\n",
+ "options": "Cashier-closing-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -43,10 +45,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,10 +78,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -105,10 +111,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -135,10 +143,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -166,10 +176,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -188,7 +200,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -197,10 +209,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -219,7 +233,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -228,10 +242,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -250,7 +266,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -259,10 +275,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -291,10 +309,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -321,10 +341,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -351,6 +373,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
}
],
@@ -364,7 +387,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-09-03 10:59:54.500567",
+ "modified": "2019-02-19 08:35:23.157327",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cashier Closing",
@@ -373,7 +396,6 @@
"permissions": [
{
"amend": 0,
- "apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
@@ -399,5 +421,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json b/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json
index bdfc70f..7f16bea 100644
--- a/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json
+++ b/erpnext/accounts/doctype/cashier_closing_payments/cashier_closing_payments.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -14,6 +15,7 @@
"fields": [
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -41,10 +43,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -63,7 +67,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -72,6 +76,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
}
],
@@ -85,7 +90,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-09-02 14:45:36.303520",
+ "modified": "2019-02-19 08:34:20.268037",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cashier Closing Payments",
@@ -99,5 +104,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 5a827be..32e49db 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -398,7 +399,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "6",
+ "precision": "9",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -911,7 +912,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-08-19 04:08:44.742510",
+ "modified": "2019-02-18 19:00:53.662788",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 7f1f550..8161cc6 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -6,7 +6,7 @@
import frappe, erpnext, json
from frappe import _, scrub, ValidationError
from frappe.utils import flt, comma_or, nowdate, getdate
-from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
+from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on, get_allow_cost_center_in_entry_of_bs_account
from erpnext.accounts.party import get_party_account
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.setup.utils import get_exchange_rate
@@ -171,7 +171,7 @@
if not frappe.db.exists(self.party_type, self.party):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
- if self.party_account:
+ if self.party_account and self.party_type in ("Customer", "Supplier"):
self.validate_account_type(self.party_account,
[erpnext.get_party_account_type(self.party_type)])
@@ -564,8 +564,8 @@
.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
# Add cost center condition
- if args.get("cost_center"):
- condition += " and cost_center='%s'" % args.get("cost_center")
+ if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
+ condition += " and cost_center='%s'" % args.get("cost_center")
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account"), condition=condition)
@@ -689,7 +689,7 @@
account_currency = get_account_currency(party_account)
account_balance = get_balance_on(party_account, date, cost_center=cost_center)
- _party_name = "title" if party_type == "Student" else party_type.lower() + "_name"
+ _party_name = "title" if party_type in ("Student", "Shareholder") else party_type.lower() + "_name"
party_name = frappe.db.get_value(party_type, party, _party_name)
party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center)
if party_type in ["Customer", "Supplier"]:
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 76fe884..bff995e 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -425,7 +426,7 @@
"no_copy": 0,
"options": "currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1501,7 +1502,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-09-06 14:44:43.563367",
+ "modified": "2019-02-18 18:52:34.203239",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
index 558297f..f5c5bca 100644
--- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
@@ -8,5 +8,6 @@
frm.add_fetch("payment_term", "due_date_based_on", "due_date_based_on");
frm.add_fetch("payment_term", "credit_days", "credit_days");
frm.add_fetch("payment_term", "credit_months", "credit_months");
+ frm.add_fetch("payment_term", "mode_of_payment", "mode_of_payment");
}
});
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index fe99763..ac0cd7e 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -196,8 +196,9 @@
pricing_rule_rate = 0.0
if pricing_rule.currency == args.currency:
pricing_rule_rate = pricing_rule.rate
+
item_details.update({
- "price_list_rate": pricing_rule_rate,
+ "price_list_rate": pricing_rule_rate * args.get("conversion_factor"),
"discount_percentage": 0.0
})
else:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 263b5bb..b4fd91f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -9,9 +9,12 @@
this.setup_posting_date_time_check();
this._super(doc);
- // formatter for material request item
- this.frm.set_indicator_formatter('item_code',
- function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
+ // formatter for purchase invoice item
+ if(this.frm.doc.update_stock) {
+ this.frm.set_indicator_formatter('item_code', function(doc) {
+ return (doc.qty<=doc.received_qty) ? "green" : "orange";
+ });
+ }
},
onload: function() {
this._super();
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 0dd716d..a9e8fbc 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -46,6 +46,7 @@
}]
def onload(self):
+ super(PurchaseInvoice, self).onload()
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
self.set_onload("supplier_tds", supplier_tds)
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index c2309b2..6da171f 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -293,7 +293,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -321,7 +321,7 @@
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
- "label": "Qty",
+ "label": "Accepted Qty",
"length": 0,
"no_copy": 0,
"oldfieldname": "qty",
@@ -358,7 +358,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2626,7 +2626,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2019-01-07 16:52:00.749414",
+ "modified": "2019-02-18 19:03:19.250280",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/italy.js b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
new file mode 100644
index 0000000..1c47d3a
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_invoice/regional/italy.js
@@ -0,0 +1,3 @@
+{% include "erpnext/regional/italy/sales_invoice.js" %}
+
+erpnext.setup_e_invoice_button('Sales Invoice')
\ 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 8911ddf..b1a851a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -201,7 +201,8 @@
get_query: function() {
var filters = {
docstatus: 1,
- company: me.frm.doc.company
+ company: me.frm.doc.company,
+ is_return: 0
};
if(me.frm.doc.customer) filters["customer"] = me.frm.doc.customer;
return {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 13ba053..077d995 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1966,7 +1966,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -5644,7 +5644,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2019-01-07 16:51:53.914523",
+ "modified": "2019-02-18 18:56:51.265257",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 895ca07..4cf3a1a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1012,9 +1012,10 @@
for serial_no in item.serial_no.split("\n"):
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
if sales_invoice and self.name != sales_invoice:
- frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}".format(
- serial_no, sales_invoice
- )))
+ sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
+ if sales_invoice_company == self.company:
+ frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
+ .format(serial_no, sales_invoice)))
def update_project(self):
if self.project:
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index d6ce115..a95f314 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -779,7 +779,7 @@
"no_copy": 0,
"options": "currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -913,7 +913,7 @@
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -2766,7 +2766,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2019-01-07 16:51:55.018091",
+ "modified": "2019-02-18 18:59:52.223628",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
index 438328d..ccdabfe 100644
--- a/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
+++ b/erpnext/accounts/doctype/sales_invoice_payment/sales_invoice_payment.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -13,6 +14,7 @@
"fields": [
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -40,11 +42,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -72,16 +75,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "0.0",
+ "default": "",
"depends_on": "eval:parent.doctype == 'Sales Invoice'",
"fieldname": "amount",
"fieldtype": "Currency",
@@ -97,7 +101,7 @@
"no_copy": 0,
"options": "currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -106,11 +110,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -136,11 +141,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -168,16 +174,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fetch_from": "mode_of_payment.type",
+ "fetch_from": "mode_of_payment.type",
"fieldname": "type",
"fieldtype": "Read Only",
"hidden": 0,
@@ -201,11 +208,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -233,11 +241,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -264,7 +273,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
}
],
@@ -278,7 +287,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-05-16 22:42:52.033991",
+ "modified": "2019-02-18 15:03:59.720469",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Payment",
@@ -292,5 +301,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index 50eed24..f7b9aef 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -1,5 +1,7 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -12,6 +14,8 @@
"engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -39,9 +43,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -68,9 +75,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -88,7 +98,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -97,9 +107,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -126,20 +139,21 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
-
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2017-02-17 16:47:04.413420",
+ "modified": "2019-02-18 18:50:44.770361",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",
@@ -153,5 +167,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 95cb351..b8c9854 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -495,6 +495,7 @@
values.append(self.filters.get(party_type_field))
if party_type_field=="customer":
+ account_type = "Receivable"
if self.filters.get("customer_group"):
lft, rgt = frappe.db.get_value("Customer Group",
self.filters.get("customer_group"), ["lft", "rgt"])
@@ -529,12 +530,18 @@
or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
- if party_type_field=="supplier":
+ elif party_type_field=="supplier":
+ account_type = "Payable"
if self.filters.get("supplier_group"):
conditions.append("""party in (select name from tabSupplier
where supplier_group=%s)""")
values.append(self.filters.get("supplier_group"))
+ accounts = [d.name for d in frappe.get_all("Account",
+ filters={"account_type": account_type, "company": self.filters.company})]
+ conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
+ values += accounts
+
return " and ".join(conditions), values
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html
index 4b3b5f2..449fb84 100644
--- a/erpnext/accounts/report/financial_statements.html
+++ b/erpnext/accounts/report/financial_statements.html
@@ -15,7 +15,7 @@
height: 37px;
}
</style>
-{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
+{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index fd84bd0..e9aecfb 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -273,7 +273,7 @@
for period in period_list:
total_row.setdefault(period.key, 0.0)
total_row[period.key] += row.get(period.key, 0.0)
- row[period.key] = 0.0
+ row[period.key] = row.get(period.key, 0.0)
total_row.setdefault("total", 0.0)
total_row["total"] += flt(row["total"])
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 8c3deaf..ecb18f7 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -53,7 +53,7 @@
frappe.throw(_("Can not filter based on Account, if grouped by Account"))
if (filters.get("voucher_no")
- and filters.get("group_by") in [_('Group by Voucher'), _('Group by Voucher (Consolidated)')]):
+ and filters.get("group_by") in [_('Group by Voucher')]):
frappe.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
if filters.from_date > filters.to_date:
@@ -101,7 +101,7 @@
frappe.db.get_value(filters.party_type, filters.party[0], "default_currency"))
filters["account_currency"] = account_currency or filters.company_currency
- if filters.account_currency != filters.company_currency:
+ if filters.account_currency != filters.company_currency and not filters.presentation_currency:
filters.presentation_currency = filters.account_currency
return filters
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 01211a9..073516f 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -6,7 +6,7 @@
from frappe import _, scrub
from erpnext.stock.utils import get_incoming_rate
from erpnext.controllers.queries import get_match_cond
-from frappe.utils import flt
+from frappe.utils import flt, cint
def execute(filters=None):
@@ -106,11 +106,14 @@
self.grouped = {}
self.grouped_data = []
+ self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
+ self.float_precision = cint(frappe.db.get_default("float_precision")) or 2
+
for row in self.si_list:
if self.skip_row(row, self.product_bundles):
continue
- row.base_amount = flt(row.base_net_amount)
+ row.base_amount = flt(row.base_net_amount, self.currency_precision)
product_bundles = []
if row.update_stock:
@@ -129,15 +132,15 @@
# get buying rate
if row.qty:
- row.buying_rate = row.buying_amount / row.qty
- row.base_rate = row.base_amount / row.qty
+ row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision)
+ row.base_rate = flt(row.base_amount / row.qty, self.float_precision)
else:
row.buying_rate, row.base_rate = 0.0, 0.0
# calculate gross profit
- row.gross_profit = row.base_amount - row.buying_amount
+ row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
if row.base_amount:
- row.gross_profit_percent = (row.gross_profit / row.base_amount) * 100.0
+ row.gross_profit_percent = flt((row.gross_profit / row.base_amount) * 100.0, self.currency_precision)
else:
row.gross_profit_percent = 0.0
@@ -156,8 +159,8 @@
new_row = row
else:
new_row.qty += row.qty
- new_row.buying_amount += row.buying_amount
- new_row.base_amount += row.base_amount
+ new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
+ new_row.base_amount += flt(row.base_amount, self.currency_precision)
new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row)
else:
@@ -167,18 +170,19 @@
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
for returned_item_row in returned_item_rows:
row.qty += returned_item_row.qty
- row.base_amount += returned_item_row.base_amount
- row.buying_amount = row.qty * row.buying_rate
+ row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
+ row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
if row.qty or row.base_amount:
row = self.set_average_rate(row)
self.grouped_data.append(row)
def set_average_rate(self, new_row):
- new_row.gross_profit = new_row.base_amount - new_row.buying_amount
- new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \
+ new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
+ new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) \
if new_row.base_amount else 0
- new_row.buying_rate = (new_row.buying_amount / new_row.qty) if new_row.qty else 0
- new_row.base_rate = (new_row.base_amount / new_row.qty) if new_row.qty else 0
+ new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
+ new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
+
return new_row
def get_returned_invoice_items(self):
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 5d24096..e33b90d 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -193,7 +193,7 @@
pi_items = frappe.db.sql("""
select parent, purchase_order, purchase_receipt, po_detail, project
from `tabPurchase Invoice Item`
- where parent in (%s) and (ifnull(purchase_order, '') != '' or ifnull(purchase_receipt, '') != '')
+ where parent in (%s)
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
invoice_po_pr_map = {}
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index a8ae94f..8a39744 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -104,7 +104,7 @@
credit_in_account_currency = flt(entry['credit_in_account_currency'])
account_currency = entry['account_currency']
- if account_currency != presentation_currency or (account_currency == presentation_currency and not is_p_or_l_account(account)):
+ if account_currency != presentation_currency:
value = debit or credit
date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 58e3e87..d4e1840 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -615,7 +615,7 @@
return held_invoices
-def get_outstanding_invoices(party_type, party, account, condition=None, limit=1000):
+def get_outstanding_invoices(party_type, party, account, condition=None, limit=None):
outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
@@ -628,7 +628,7 @@
invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice'
held_invoices = get_held_invoices(party_type, party)
- limit_cond = "limit %s" % (limit or 1000)
+ limit_cond = "limit %s" % limit if limit else ""
invoice_list = frappe.db.sql("""
select
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 6b3c3cc73..bbe92f6 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -1812,8 +1812,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_from": "company.default_finance_book",
"fieldname": "default_finance_book",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1824,12 +1825,12 @@
"label": "Default Finance Book",
"length": 0,
"no_copy": 0,
- "options": "company.default_finance_book",
+ "options": "Finance Book",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -1882,7 +1883,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-01-15 16:12:48.314196",
+ "modified": "2019-02-12 11:29:01.747819",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 3c4ef2b..a4a636d 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -456,7 +456,7 @@
items_dict = {
rm_item_code: {
"item_name": rm_item_data["item_name"],
- "description": item_wh[rm_item_code].get('description'),
+ "description": item_wh.get(rm_item_code, {}).get('description', ""),
'qty': rm_item_data["qty"],
'from_warehouse': rm_item_data["warehouse"],
'stock_uom': rm_item_data["stock_uom"],
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
index 94d93f6..3c775cd 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -848,7 +848,7 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
@@ -1786,7 +1786,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2019-01-07 16:52:02.125715",
+ "modified": "2019-02-18 18:58:10.351451",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.json b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
index 996e3ee..7ce779d 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.json
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-05 16:08:24.156448",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-10-05 16:08:33.272201",
+ "modified": "2019-02-12 14:32:29.107109",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Analytics",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0ba47ed..34bbe7b 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -94,6 +94,8 @@
if self.is_return:
self.validate_qty()
+ validate_regional(self)
+
def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates()
self.set_due_date()
@@ -1132,3 +1134,7 @@
p_doctype.update_blanket_order()
p_doctype.update_billing_percentage()
p_doctype.set_status()
+
+@erpnext.allow_regional
+def validate_regional(doc):
+ pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
index df56918..26564a3 100644
--- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
+++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@@ -55,7 +56,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -71,7 +72,7 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -707,7 +708,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-08-08 13:00:06.260997",
+ "modified": "2019-02-12 11:37:18.713344",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Clinical Procedure Template",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 7d77f90..ccdd412 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -202,7 +202,8 @@
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
},
"Sales Invoice": {
- "on_submit": "erpnext.regional.france.utils.create_transaction_log",
+ "on_submit": ["erpnext.regional.france.utils.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit"],
+ "on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel",
"on_trash": "erpnext.regional.check_deletion_permission"
},
"Payment Entry": {
@@ -210,7 +211,7 @@
"on_trash": "erpnext.regional.check_deletion_permission"
},
'Address': {
- 'validate': 'erpnext.regional.india.utils.validate_gstin_for_india'
+ 'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code']
},
('Sales Invoice', 'Purchase Invoice', 'Delivery Note'): {
'validate': 'erpnext.regional.india.utils.set_place_of_supply'
@@ -301,5 +302,9 @@
},
'Saudi Arabia': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
+ },
+ 'Italy': {
+ 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
+ 'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
}
}
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index c73df62..f4285a2 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -19,7 +19,6 @@
filters: {
"root_type": "Asset",
"is_group": 0,
- "account_type": "Payable",
"company": frm.doc.company
}
};
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 6bdab61..6da7184 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -35,6 +35,7 @@
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name');
+cur_frm.add_fetch('expense_type','description','description');
cur_frm.cscript.onload = function(doc) {
if (doc.__islocal) {
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
index b808990..d4e7057 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
@@ -1,366 +1,378 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:27:46",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 1,
+ "allow_copy": 0,
+ "allow_events_in_timeline": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2013-02-22 01:27:46",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "editable_grid": 1,
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "expense_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Expense Date",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "expense_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "150px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "expense_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Expense Date",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "expense_date",
+ "oldfieldtype": "Date",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
- "unique": 0,
+ "unique": 0,
"width": "150px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "expense_type",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Expense Claim Type",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "expense_type",
- "oldfieldtype": "Link",
- "options": "Expense Claim Type",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "150px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "expense_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Expense Claim Type",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "expense_type",
+ "oldfieldtype": "Link",
+ "options": "Expense Claim Type",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
- "unique": 0,
+ "unique": 0,
"width": "150px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "expense_type",
- "fieldname": "default_account",
- "fieldtype": "Link",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Default Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "expense_type",
+ "fieldname": "default_account",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Default Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_4",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "expense_type.description",
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "description",
- "oldfieldtype": "Small Text",
- "options": "",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "300px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_from": "",
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "description",
+ "oldfieldtype": "Small Text",
+ "options": "",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "300px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
- "unique": 0,
+ "unique": 0,
"width": "300px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "claim_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Claim Amount",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "claim_amount",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "150px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "claim_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Claim Amount",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "claim_amount",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
- "unique": 0,
+ "unique": 0,
"width": "150px"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_8",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sanctioned_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Sanctioned Amount",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "sanctioned_amount",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "print_width": "150px",
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sanctioned_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Sanctioned Amount",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "sanctioned_amount",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"translatable": 0,
- "unique": 0,
+ "unique": 0,
"width": "150px"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-05-16 22:42:56.727863",
- "modified_by": "Administrator",
- "module": "HR",
- "name": "Expense Claim Detail",
- "owner": "harshada@webnotestech.com",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 1,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2019-02-24 08:41:36.122565",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Expense Claim Detail",
+ "owner": "harshada@webnotestech.com",
+ "permissions": [],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 6b7c0f7..b85f38b 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -487,7 +487,6 @@
add_block_dates(events, start, end, employee, company)
add_holidays(events, start, end, employee, company)
-
return events
def add_department_leaves(events, start, end, employee, company):
@@ -500,10 +499,22 @@
department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s
and company=%s""", (department, company))
- match_conditions = "and employee in (\"%s\")" % '", "'.join(department_employees)
- add_leaves(events, start, end, match_conditions=match_conditions)
+ filter_conditions = "employee in (\"%s\")" % '", "'.join(department_employees)
+ add_leaves(events, start, end, filter_conditions=filter_conditions)
-def add_leaves(events, start, end, match_conditions=None):
+def add_leaves(events, start, end, filter_conditions=None):
+ conditions = []
+
+ if filter_conditions:
+ conditions.append(filter_conditions)
+
+ if not cint(frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")):
+ from frappe.desk.reportview import build_match_conditions
+ match_conditions = build_match_conditions("Leave Application")
+
+ if match_conditions:
+ conditions.append(match_conditions)
+
query = """select name, from_date, to_date, employee_name, half_day,
status, employee, docstatus
from `tabLeave Application` where
@@ -511,8 +522,8 @@
and docstatus < 2
and status!="Rejected" """
- if match_conditions:
- query += match_conditions
+ if conditions:
+ query += ' and ' + ' and '.join(conditions)
for d in frappe.db.sql(query, {"start":start, "end": end}, as_dict=True):
e = {
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index 83afa8c..c9a5d87 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -195,7 +195,7 @@
"columns": 0,
"fetch_from": "employee.branch",
"fieldname": "branch",
- "fieldtype": "Read Only",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -208,11 +208,11 @@
"no_copy": 0,
"oldfieldname": "branch",
"oldfieldtype": "Link",
- "options": "",
+ "options": "Branch",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -901,7 +901,7 @@
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 1,
"read_only": 0,
@@ -1906,7 +1906,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-01-30 11:28:11.774739",
+ "modified": "2019-02-18 18:54:36.161027",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json
index 7a9393c..797f8f7 100644
--- a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json
+++ b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json
@@ -1,5 +1,7 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -11,16 +13,21 @@
"editable_grid": 1,
"fields": [
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "time_sheet",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Time Sheet",
"length": 0,
"no_copy": 0,
@@ -30,49 +37,58 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "working_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Working Hours",
"length": 0,
"no_copy": 1,
"permlevel": 0,
- "precision": "3",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
-
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-07-11 03:28:07.152366",
+ "modified": "2019-02-19 08:33:41.762144",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Timesheet",
@@ -82,7 +98,10 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
+ "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_seen": 0
+ "track_changes": 0,
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json
index ce8b64e..0e47278 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.json
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@@ -365,7 +366,7 @@
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -950,7 +951,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-07-24 18:36:25.169098",
+ "modified": "2019-02-18 18:51:53.932518",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
index 1283711..4b49f19 100644
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
+++ b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -351,8 +352,8 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-09-01 17:05:59.600583",
- "modified_by": "cave@aperture.com",
+ "modified": "2019-02-01 14:21:16.729848",
+ "modified_by": "Administrator",
"module": "Hub Node",
"name": "Marketplace Settings",
"name_case": "",
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
index 6d082bd..2f2ad00 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -41,12 +41,19 @@
work_done = nm and nm[0][3] or ''
else:
status = 'Open'
- mntc_date = ''
- service_person = ''
- work_done = ''
+ mntc_date = None
+ service_person = None
+ work_done = None
- frappe.db.sql("update `tabWarranty Claim` set resolution_date=%s, resolved_by=%s, resolution_details=%s, status=%s where name =%s",(mntc_date,service_person,work_done,status,d.prevdoc_docname))
+ wc_doc = frappe.get_doc('Warranty Claim', d.prevdoc_docname)
+ wc_doc.update({
+ 'resolution_date': mntc_date,
+ 'resolved_by': service_person,
+ 'resolution_details': work_done,
+ 'status': status
+ })
+ wc_doc.db_update()
def check_if_last_visit(self):
"""check if last maintenance visit against same sales order/ Warranty Claim"""
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index ba3b7192..ea6b7ed 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -183,7 +183,7 @@
args = frappe._dict({
"doctype": "BOM",
"price_list": self.buying_price_list,
- "qty": arg.get("qty"),
+ "qty": arg.get("qty") or 1,
"uom": arg.get("uom") or arg.get("stock_uom"),
"stock_uom": arg.get("stock_uom"),
"transaction_type": "buying",
@@ -706,8 +706,11 @@
def get_boms_in_bottom_up_order(bom_no=None):
def _get_parent(bom_no):
- return frappe.db.sql_list("""select distinct parent from `tabBOM Item`
- where bom_no = %s and docstatus=1 and parenttype='BOM'""", bom_no)
+ return frappe.db.sql_list("""
+ select distinct bom_item.parent from `tabBOM Item` bom_item
+ where bom_item.bom_no = %s and bom_item.docstatus=1 and bom_item.parenttype='BOM'
+ and exists(select bom.name from `tabBOM` bom where bom.name=bom_item.parent and bom.is_active=1)
+ """, bom_no)
count = 0
bom_list = []
@@ -715,9 +718,10 @@
bom_list.append(bom_no)
else:
# get all leaf BOMs
- bom_list = frappe.db.sql_list("""select name from `tabBOM` bom where docstatus=1
- and not exists(select bom_no from `tabBOM Item`
- where parent=bom.name and ifnull(bom_no, '')!='')""")
+ bom_list = frappe.db.sql_list("""select name from `tabBOM` bom
+ where docstatus=1 and is_active=1
+ and not exists(select bom_no from `tabBOM Item`
+ where parent=bom.name and ifnull(bom_no, '')!='')""")
while(count < len(bom_list)):
for child_bom in _get_parent(bom_list[count]):
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json
index 2258360..febf315 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.json
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json
@@ -85,6 +85,39 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "operation",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Item operation",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Operation",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -900,6 +933,38 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "allow_alternative_item",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Allow Alternative Item",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fetch_from": "item_code.include_item_in_manufacturing",
"fieldname": "include_item_in_manufacturing",
"fieldtype": "Check",
@@ -958,71 +1023,6 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "operation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Item operation",
- "length": 0,
- "no_copy": 0,
- "options": "Operation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allow_alternative_item",
- "fieldtype": "Check",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Allow Alternative Item",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
}
],
"has_web_view": 0,
@@ -1035,7 +1035,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-11-23 15:05:55.187136",
+ "modified": "2019-02-21 19:19:54.872459",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",
@@ -1050,4 +1050,4 @@
"track_changes": 0,
"track_seen": 0,
"track_views": 0
-}
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 5ed03be..ea9f714 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -172,9 +172,6 @@
doclist = get_mapped_doc("Job Card", source_name, {
"Job Card": {
"doctype": "Material Request",
- "validation": {
- "docstatus": ["=", 1]
- },
"field_map": {
"name": "job_card",
},
@@ -206,9 +203,6 @@
doclist = get_mapped_doc("Job Card", source_name, {
"Job Card": {
"doctype": "Stock Entry",
- "validation": {
- "docstatus": ["=", 1]
- },
"field_map": {
"name": "job_card",
"for_quantity": "fg_completed_qty"
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index d17adf6..97a8ea7 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -276,8 +276,8 @@
item_dict[(d.item_code, d.material_request_item, d.warehouse)] = item_details
else:
item_details.update({
- "qty":flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{})
- .get("qty")) + flt(d.planned_qty)
+ "qty": flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{})
+ .get("qty")) + (flt(d.planned_qty) - flt(d.ordered_qty))
})
item_dict[(d.item_code, d.sales_order, d.warehouse)] = item_details
diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.json b/erpnext/manufacturing/report/production_analytics/production_analytics.json
index 023e0a8..946cd33 100644
--- a/erpnext/manufacturing/report/production_analytics/production_analytics.json
+++ b/erpnext/manufacturing/report/production_analytics/production_analytics.json
@@ -1,13 +1,14 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-11 19:28:37.085066",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "",
- "modified": "2018-10-11 19:28:37.085066",
+ "modified": "2019-02-12 14:32:16.392521",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Analytics",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 29c8619..433141c 100755
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -488,7 +488,6 @@
erpnext.patches.v10_0.update_assessment_result
erpnext.patches.v10_0.set_default_payment_terms_based_on_company
erpnext.patches.v10_0.update_sales_order_link_to_purchase_order
-erpnext.patches.v10_0.item_barcode_childtable_migrate
erpnext.patches.v10_0.rename_price_to_rate_in_pricing_rule
erpnext.patches.v10_0.set_currency_in_pricing_rule
erpnext.patches.v10_0.set_b2c_limit
@@ -585,3 +584,7 @@
erpnext.patches.v11_0.renamed_from_to_fields_in_project
erpnext.patches.v11_0.add_permissions_in_gst_settings
erpnext.patches.v11_1.setup_guardian_role
+execute:frappe.delete_doc('DocType', 'Notification Control')
+erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants
+erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019
+erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019
diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
index c939dd5..bc60056 100644
--- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
+++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
@@ -7,20 +7,25 @@
def execute():
- items_barcode = frappe.get_all('Item', ['name', 'barcode'], { 'barcode': ('!=', '') })
-
- frappe.reload_doc("stock", "doctype", "item")
frappe.reload_doc("stock", "doctype", "item_barcode")
+ items_barcode = frappe.get_all('Item', ['name', 'barcode'], { 'barcode': ('!=', '') })
+ frappe.reload_doc("stock", "doctype", "item")
+
+
+
for item in items_barcode:
barcode = item.barcode.strip()
if barcode and '<' not in barcode:
- frappe.get_doc({
- 'idx': 0,
- 'doctype': 'Item Barcode',
- 'barcode': barcode,
- 'parenttype': 'Item',
- 'parent': item.name,
- 'parentfield': 'barcodes'
- }).insert()
+ try:
+ frappe.get_doc({
+ 'idx': 0,
+ 'doctype': 'Item Barcode',
+ 'barcode': barcode,
+ 'parenttype': 'Item',
+ 'parent': item.name,
+ 'parentfield': 'barcodes'
+ }).insert()
+ except frappe.DuplicateEntryError:
+ continue
diff --git a/erpnext/patches/v11_0/make_italian_localization_fields.py b/erpnext/patches/v11_0/make_italian_localization_fields.py
new file mode 100644
index 0000000..44a281f
--- /dev/null
+++ b/erpnext/patches/v11_0/make_italian_localization_fields.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+from erpnext.regional.italy.setup import make_custom_fields, setup_report
+from erpnext.regional.italy import state_codes
+import frappe
+
+
+def execute():
+ company = frappe.get_all('Company', filters = {'country': 'Italy'})
+ if not company:
+ return
+
+ frappe.reload_doc('regional', 'report', 'electronic_invoice_register')
+ make_custom_fields()
+ setup_report()
+
+ # Set state codes
+ condition = ""
+ for state, code in state_codes.items():
+ condition += " when '{0}' then '{1}'".format(frappe.db.escape(state), frappe.db.escape(code))
+
+ if condition:
+ condition = "state_code = (case state {0} end),".format(condition)
+
+ frappe.db.sql("""
+ UPDATE tabAddress set {condition} country_code = UPPER(ifnull((select code
+ from `tabCountry` where name = `tabAddress`.country), ''))
+ """.format(condition=condition))
diff --git a/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py b/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py
new file mode 100644
index 0000000..97ddd41
--- /dev/null
+++ b/erpnext/patches/v11_0/remove_barcodes_field_from_copy_fields_to_variants.py
@@ -0,0 +1,7 @@
+import frappe
+
+def execute():
+ '''Remove barcodes field from "Copy Fields to Variants" table because barcodes must be unique'''
+
+ settings = frappe.get_doc('Item Variant Settings')
+ settings.remove_invalid_fields_for_copy_fields_in_variants()
diff --git a/erpnext/patches/v11_0/update_total_qty_field.py b/erpnext/patches/v11_0/update_total_qty_field.py
index fcb76af..992454a 100644
--- a/erpnext/patches/v11_0/update_total_qty_field.py
+++ b/erpnext/patches/v11_0/update_total_qty_field.py
@@ -19,9 +19,10 @@
SELECT
parent, SUM(qty) as qty
FROM
- `tab%s Item`
+ `tab{0} Item`
+ where parenttype = '{0}'
GROUP BY parent
- ''' % (doctype), as_dict = True)
+ '''.format(doctype), as_dict = True)
# Query to update total_qty might become too big, Update in batches
# batch_size is chosen arbitrarily, Don't try too hard to reason about it
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 3b42f6a6..e3fd1a8 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -66,11 +66,11 @@
def validate(self):
self.validate_project_name()
- self.validate_dates()
self.validate_weights()
self.sync_tasks()
self.tasks = []
self.load_tasks()
+ self.validate_dates()
self.send_welcome_email()
self.update_percent_complete()
@@ -79,6 +79,24 @@
frappe.throw(_("Project {0} already exists").format(frappe.safe_decode(self.project_name)))
def validate_dates(self):
+ if self.tasks:
+ for d in self.tasks:
+ if self.expected_start_date:
+ if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date):
+ frappe.throw(_("Start date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_start_date))
+ if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date):
+ frappe.throw(_("End date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_start_date))
+
+ if self.expected_end_date:
+ if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date):
+ frappe.throw(_("Start date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_end_date))
+ if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date):
+ frappe.throw(_("End date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
+ .format(d.title, self.name, self.expected_end_date))
+
if self.expected_start_date and self.expected_end_date:
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
frappe.throw(_("Expected End Date can not be less than Expected Start Date"))
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 371fc5c..fffa9c1 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -44,12 +44,6 @@
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
- if(self.project):
- if frappe.db.exists("Project", self.project):
- expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
- if self.exp_end_date and expected_end_date and getdate(self.exp_end_date) > getdate(expected_end_date) :
- frappe.throw(_("Expected end date cannot be after Project: <b>'{0}'</b> Expected end date").format(self.project), EndDateCannotBeGreaterThanProjectEndDateError)
-
def validate_status(self):
if self.status!=self.get_db_value("status") and self.status == "Closed":
for d in self.depends_on:
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 6fb5412..9971946 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -5,7 +5,7 @@
import unittest
from frappe.utils import getdate, nowdate, add_days
-from erpnext.projects.doctype.task.task import CircularReferenceError, EndDateCannotBeGreaterThanProjectEndDateError
+from erpnext.projects.doctype.task.task import CircularReferenceError
class TestTask(unittest.TestCase):
def test_circular_reference(self):
@@ -97,15 +97,6 @@
self.assertEqual(frappe.db.get_value("Task", task.name, "status"), "Overdue")
- def test_end_date_validation(self):
- task_end = create_task("Testing_Enddate_validation", add_days(nowdate(), 35), add_days(nowdate(), 45), save=False)
- pro = frappe.get_doc("Project", task_end.project)
- pro.expected_end_date = add_days(nowdate(), 40)
- pro.save()
- self.assertRaises(EndDateCannotBeGreaterThanProjectEndDateError, task_end.save)
-
-
-
def create_task(subject, start=None, end=None, depends_on=None, project=None, save=True):
if not frappe.db.exists("Task", subject):
task = frappe.new_doc('Task')
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index e890bef..8811ab9 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -18,7 +18,7 @@
return{
filters: {
'project': child.project,
- 'status': ["!=", "Closed"]
+ 'status': ["!=", "Cancelled"]
}
}
}
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
index b1f7372..a9b3bfb 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -13,6 +14,7 @@
"fields": [
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -44,6 +46,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -75,6 +78,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -105,6 +109,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -136,6 +141,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -166,6 +172,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -197,6 +204,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -230,6 +238,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -260,6 +269,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -278,7 +288,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -292,6 +302,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -325,6 +336,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -355,6 +367,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -388,6 +401,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -420,6 +434,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -450,6 +465,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -482,6 +498,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -512,6 +529,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -545,6 +563,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -575,6 +594,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -607,6 +627,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -637,6 +658,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -669,6 +691,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -700,6 +723,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -718,7 +742,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 1,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -732,6 +756,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -752,7 +777,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 1,
- "precision": "2",
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -766,6 +791,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -796,6 +822,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -827,6 +854,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -860,6 +888,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -891,6 +920,7 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -932,7 +962,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-05-07 15:12:31.510813",
+ "modified": "2019-02-18 18:55:53.190526",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet Detail",
@@ -944,5 +974,6 @@
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 0,
- "track_seen": 0
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index 1b8e079..1c12c35 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -73,7 +73,7 @@
from_date: me.frm.doc.posting_date,
to_date: me.frm.doc.posting_date,
company: me.frm.doc.company,
- group_by: ""
+ group_by: "Group by Voucher (Consolidated)"
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3751d7b..cf62af7 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -276,13 +276,11 @@
scan_barcode: function() {
let scan_barcode_field = this.frm.fields_dict["scan_barcode"];
- let show_description = function(idx, item_code, exist=null) {
- if(exist) {
- scan_barcode_field.set_new_description(__('Row : ') + idx + ' ' +
- item_code + __(' Qty increased by 1'));
+ let show_description = function(idx, exist = null) {
+ if (exist) {
+ scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx]));
} else {
- scan_barcode_field.set_new_description(__('New row : ') + idx + ' ' +
- item_code + __(' Created'));
+ scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx]));
}
}
@@ -291,39 +289,39 @@
method: "erpnext.selling.page.point_of_sale.point_of_sale.search_serial_or_batch_or_barcode_number",
args: { search_value: this.frm.doc.scan_barcode }
}).then(r => {
-
- if(r && r.message && r.message.item_code) {
- let child = "";
- let add_row_index = -1;
- let cur_grid= this.frm.fields_dict["items"].grid;
-
- this.frm.doc.items.map(d => {
- if(d.item_code==r.message.item_code){
- add_row_index = d.idx;
- return;
- } else if(!d.item_code && add_row_index==-1) {
- add_row_index = d.idx;
- }
- });
-
- if(add_row_index == -1) {
- child = frappe.model.add_child(this.frm.doc, cur_grid.doctype, "items", add_row_index);
- } else {
- child = cur_grid.get_grid_row(add_row_index-1).doc;
- }
- show_description(child.idx, r.message.item_code, child.item_code);
-
- frappe.model.set_value(child.doctype, child.name, {
- item_code: r.message.item_code,
- qty: (child.qty || 0) + 1,
- barcode: r.message.barcode
- });
+ const data = r && r.message;
+ if (!data) {
+ scan_barcode_field.set_new_description(__('Cannot find Item with this barcode'));
+ return;
}
- else{
- scan_barcode_field.set_new_description(this.frm.doc.scan_barcode +__(' does not exist!'));
+
+ let cur_grid = this.frm.fields_dict.items.grid;
+
+ let row_to_modify = null;
+ const existing_item_row = this.frm.doc.items.find(d => d.item_code === data.item_code);
+ const blank_item_row = this.frm.doc.items.find(d => !d.item_code);
+
+ if (existing_item_row) {
+ row_to_modify = existing_item_row;
+ } else if (blank_item_row) {
+ row_to_modify = blank_item_row;
}
+
+ if (!row_to_modify) {
+ // add new row
+ row_to_modify = frappe.model.add_child(this.frm.doc, cur_grid.doctype, 'items');
+ }
+
+ show_description(row_to_modify.idx, row_to_modify.item_code);
+
+ frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
+ item_code: data.item_code,
+ qty: (row_to_modify.qty || 0) + 1
+ });
+
+ this.frm.refresh_field('items');
});
- scan_barcode_field.set_value("");
+ scan_barcode_field.set_value('');
}
return false;
},
diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue
index a101eaf..f151add 100644
--- a/erpnext/public/js/hub/PageContainer.vue
+++ b/erpnext/public/js/hub/PageContainer.vue
@@ -10,14 +10,15 @@
import Search from './pages/Search.vue';
import Category from './pages/Category.vue';
import SavedItems from './pages/SavedItems.vue';
+import FeaturedItems from './pages/FeaturedItems.vue';
import PublishedItems from './pages/PublishedItems.vue';
import Item from './pages/Item.vue';
import Seller from './pages/Seller.vue';
+import SellerItems from './pages/SellerItems.vue';
import Publish from './pages/Publish.vue';
import Buying from './pages/Buying.vue';
import Selling from './pages/Selling.vue';
import Messages from './pages/Messages.vue';
-import Profile from './pages/Profile.vue';
import NotFound from './pages/NotFound.vue';
function get_route_map() {
@@ -27,11 +28,13 @@
'marketplace/category/:category': Category,
'marketplace/item/:item': Item,
'marketplace/seller/:seller': Seller,
+ 'marketplace/seller/:seller/items': SellerItems,
'marketplace/not-found': NotFound,
}
const registered_routes = {
- 'marketplace/profile': Profile,
+ 'marketplace/profile': Seller,
'marketplace/saved-items': SavedItems,
+ 'marketplace/featured-items': FeaturedItems,
'marketplace/publish': Publish,
'marketplace/published-items': PublishedItems,
'marketplace/buying': Buying,
diff --git a/erpnext/public/js/hub/Sidebar.vue b/erpnext/public/js/hub/Sidebar.vue
index ef3510d..66c291e 100644
--- a/erpnext/public/js/hub/Sidebar.vue
+++ b/erpnext/public/js/hub/Sidebar.vue
@@ -31,6 +31,11 @@
condition: () => this.hub_registered
},
{
+ label: __('Your Featured Items'),
+ route: 'marketplace/featured-items',
+ condition: () => this.hub_registered
+ },
+ {
label: __('Your Profile'),
route: 'marketplace/profile',
condition: () => this.hub_registered
diff --git a/erpnext/public/js/hub/components/DetailHeaderItem.vue b/erpnext/public/js/hub/components/DetailHeaderItem.vue
index 8ca4379..a6c5f06 100644
--- a/erpnext/public/js/hub/components/DetailHeaderItem.vue
+++ b/erpnext/public/js/hub/components/DetailHeaderItem.vue
@@ -1,5 +1,12 @@
<template>
- <p class="text-muted" v-html="header_item"></p>
+ <p class="text-muted" v-if="!Array.isArray(this.header_items)" v-html="header_items"></p>
+ <p class="text-muted" v-else>
+ <span v-for="(header_item , index) in header_items" :key="index">
+ <span v-if="index" v-html="spacer"></span>
+ <span v-if="typeof(header_item) == 'string'" v-html="header_item"></span>
+ <a v-else-if="typeof(header_item) == 'object'" @click="header_item.on_click(header_item.value)" v-html="header_item.value"></a>
+ </span>
+ </p>
</template>
<script>
@@ -11,9 +18,8 @@
props: ['value'],
data() {
return {
- header_item: Array.isArray(this.value)
- ? this.value.join(spacer)
- : this.value
+ header_items: this.value,
+ spacer: spacer
}
},
}
diff --git a/erpnext/public/js/hub/components/EmptyState.vue b/erpnext/public/js/hub/components/EmptyState.vue
index d6216c9..e3a33a0 100644
--- a/erpnext/public/js/hub/components/EmptyState.vue
+++ b/erpnext/public/js/hub/components/EmptyState.vue
@@ -3,7 +3,7 @@
:class="{ 'bordered': bordered, 'align-center': centered, 'justify-center': centered }"
:style="{ height: height + 'px' }"
>
- <p class="text-muted">{{ message }}</p>
+ <p class="text-muted" v-html="message" ></p>
<p v-if="action">
<button class="btn btn-default btn-xs"
@click="action.on_click"
diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue
new file mode 100644
index 0000000..ab9990a
--- /dev/null
+++ b/erpnext/public/js/hub/pages/FeaturedItems.vue
@@ -0,0 +1,118 @@
+<template>
+ <div
+ class="marketplace-page"
+ :data-page-name="page_name"
+ >
+ <h5>{{ page_title }}</h5>
+ <p v-if="items.length"
+ class="text-muted margin-bottom">
+ {{ __('You can Feature upto 8 items.') }}
+ </p>
+
+ <item-cards-container
+ :container_name="page_title"
+ :items="items"
+ :item_id_fieldname="item_id_fieldname"
+ :on_click="go_to_item_details_page"
+ :editable="true"
+ @remove-item="on_item_remove"
+ :empty_state_message="empty_state_message"
+ >
+ </item-cards-container>
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'featured-items-page',
+ data() {
+ return {
+ page_name: frappe.get_route()[1],
+ items: [],
+ item_id_fieldname: 'name',
+
+ // Constants
+ page_title: __('Your Featured Items'),
+ empty_state_message: __(`No featured items yet. Got to your
+ <a href="#marketplace/published-items">
+ Published Items</a>
+ and feature upto 8 items that you want to highlight to your customers.`)
+ };
+ },
+ created() {
+ this.get_items();
+ },
+ methods: {
+ get_items() {
+ hub.call(
+ 'get_featured_items_of_seller', {},
+ 'action:item_feature'
+ )
+ .then((items) => {
+ this.items = items;
+ })
+ },
+
+ go_to_item_details_page(hub_item_name) {
+ frappe.set_route(`marketplace/item/${hub_item_name}`);
+ },
+
+ on_item_remove(hub_item_name) {
+ const grace_period = 5000;
+ let reverted = false;
+ let alert;
+
+ const undo_remove = () => {
+ this.toggle_item(hub_item_name);;
+ reverted = true;
+ alert.hide();
+ return false;
+ }
+
+ const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
+
+ alert = frappe.show_alert(__(`<span>${item_name} removed.
+ <a href="#" data-action="undo-remove"><b>Undo</b></a></span>`),
+ grace_period/1000,
+ {
+ 'undo-remove': undo_remove.bind(this)
+ }
+ );
+
+ this.toggle_item(hub_item_name, false);
+
+ setTimeout(() => {
+ if(!reverted) {
+ this.remove_item_from_featured_items(hub_item_name);
+ }
+ }, grace_period);
+ },
+
+ remove_item_from_featured_items(hub_item_name) {
+ erpnext.hub.trigger('action:item_feature');
+ hub.call('remove_item_from_seller_featured_items', {
+ hub_item_name,
+ hub_user: frappe.session.user
+ })
+ .then(() => {
+ this.get_items();
+ })
+ .catch(e => {
+ console.log(e);
+ });
+ },
+
+ // By default show
+ toggle_item(hub_item_name, show=true) {
+ this.items = this.items.map(item => {
+ if(item.name === hub_item_name) {
+ item.seen = show;
+ }
+ return item;
+ });
+ }
+ }
+}
+</script>
+
+<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue
index 8dbd397..841d004 100644
--- a/erpnext/public/js/hub/pages/Item.vue
+++ b/erpnext/public/js/hub/pages/Item.vue
@@ -73,6 +73,11 @@
action: this.add_to_saved_items
},
{
+ label: __('Add to Featured Item'),
+ condition: hub.is_user_registered() && this.is_own_item,
+ action: this.add_to_featured_items
+ },
+ {
label: __('Report this Item'),
condition: !this.is_own_item,
action: this.report_item
@@ -103,7 +108,7 @@
subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
- subtitle_items.push(this.item.company);
+ subtitle_items.push({value:this.item.company,on_click:this.go_to_seller_profile_page});
return subtitle_items;
},
@@ -169,7 +174,9 @@
this.make_dialogs();
});
},
-
+ go_to_seller_profile_page(seller_name) {
+ frappe.set_route(`marketplace/seller/${seller_name}`);
+ },
build_data() {
this.title = this.item.item_name || this.item.name;
this.image = this.item.image;
@@ -210,6 +217,21 @@
});
},
+ add_to_featured_items() {
+ hub.call('add_item_to_seller_featured_items', {
+ hub_item_name: this.hub_item_name,
+ hub_user: frappe.session.user
+ },)
+ .then(() => {
+ const featured_items_link = `<b><a href="#marketplace/featured-items">${__('Added to Featured Items')}</a></b>`
+ frappe.show_alert(featured_items_link);
+ erpnext.hub.trigger('action:item_feature');
+ })
+ .catch(e => {
+ console.error(e);
+ });
+ },
+
make_contact_seller_dialog() {
this.contact_seller_dialog = new frappe.ui.Dialog({
title: __('Send a message'),
diff --git a/erpnext/public/js/hub/pages/Profile.vue b/erpnext/public/js/hub/pages/Profile.vue
deleted file mode 100644
index 91ed946..0000000
--- a/erpnext/public/js/hub/pages/Profile.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
- <div
- class="marketplace-page"
- :data-page-name="page_name"
- v-if="init || profile"
- >
-
- <detail-view
- :title="title"
- :image="image"
- :sections="sections"
- :show_skeleton="init"
- >
-
- <detail-header-item slot="detail-header-item"
- :value="country"
- ></detail-header-item>
- <detail-header-item slot="detail-header-item"
- :value="site_name"
- ></detail-header-item>
- <detail-header-item slot="detail-header-item"
- :value="joined_when"
- ></detail-header-item>
-
- </detail-view>
- </div>
-</template>
-
-<script>
-export default {
- name: 'profile-page',
- data() {
- return {
- page_name: frappe.get_route()[1],
-
- init: true,
-
- profile: null,
- title: null,
- image: null,
- sections: [],
-
- country: '',
- site_name: '',
- joined_when: '',
- };
- },
- created() {
- this.get_profile();
- },
- methods: {
- get_profile() {
- hub.call(
- 'get_hub_seller_profile',
- { hub_seller: hub.settings.hub_seller_name }
- ).then(profile => {
- this.init = false;
-
- this.profile = profile;
- this.title = profile.company;
-
- this.country = __(profile.country);
- this.site_name = __(profile.site_name);
- this.joined_when = __(`Joined ${comment_when(profile.creation)}`);
-
- this.image = profile.logo;
- this.sections = [
- {
- title: __('About the Company'),
- content: profile.company_description
- ? __(profile.company_description)
- : __('No description')
- }
- ];
- });
- }
- }
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue
index c80865b..e339eaa 100644
--- a/erpnext/public/js/hub/pages/Seller.vue
+++ b/erpnext/public/js/hub/pages/Seller.vue
@@ -22,30 +22,77 @@
</detail-view>
- <h5 v-if="profile">{{ item_container_heading }}</h5>
- <item-cards-container
- :container_name="item_container_heading"
- :items="items"
- :item_id_fieldname="item_id_fieldname"
- :on_click="go_to_item_details_page"
- >
- </item-cards-container>
+ <div v-if="items.length">
+ <h5>
+ {{ item_container_heading }}
+ <small v-if="is_user_registered() && is_own_company">
+ <a class="pull-right" href="#marketplace/featured-items">Customize your Featured Items</a>
+ </small>
+ </h5>
+ <item-cards-container
+ :container_name="item_container_heading"
+ :items="items"
+ :item_id_fieldname="item_id_fieldname"
+ :on_click="go_to_item_details_page"
+ >
+ </item-cards-container>
+ <a class="pull-right" @click="go_to_seller_items_page(seller_company)">Show all items</a>
+ </div>
+
+ <div v-if="recent_seller_reviews.length">
+ <h5>Customer Reviews</h5>
+ <div class="container" v-for="review in recent_seller_reviews" :key="review.name">
+ <br>
+ <span class="text-muted">
+ <rating :rating="review.rating" :max_rating="5"></rating>
+ </span>
+ <i class="octicon octicon-quote hidden-xs fa-fw"></i>
+ <span class="bold">{{ review.subject }}</span>
+ <i class="octicon octicon-quote hidden-xs fa-fw fa-rotate-180"></i>
+ <div class="container">
+ by {{ review.username }}
+ <a class="text-muted">
+ <span class="text-muted hidden-xs">–</span>
+ <span class="hidden-xs" v-html="comment_when(review.timestamp)"></span>
+ </a>
+ </div>
+ </div>
+ </div>
+
+ <div v-if="seller_product_view_stats.length">
+ <h5>Stats</h5>
+ <div id="seller_traffic_chart"></div>
+ </div>
+
+
+
</div>
</template>
<script>
+import Rating from '../components/Rating.vue';
+
+
export default {
name: 'seller-page',
+ components: {
+ Rating
+ },
data() {
return {
page_name: frappe.get_route()[1],
seller_company: frappe.get_route()[2],
+ hub_seller: null,
init: true,
profile: null,
items:[],
+ recent_seller_reviews: [],
+ seller_product_view_stats: [],
+ seller_traffic_chart: null,
item_id_fieldname: 'name',
+ item_container_heading: 'Items',
title: null,
image: null,
@@ -60,19 +107,39 @@
this.get_seller_profile_and_items();
},
computed: {
- item_container_heading() {
- return __('Items by ' + this.seller_company);
- }
+ is_own_company() {
+ let is_own_company = false;
+ if(this.hub_seller) {
+ if(this.hub_seller === hub.settings.hub_seller_name) {
+ is_own_company = true;
+ }
+ }
+ return is_own_company;
+ },
},
methods: {
+ comment_when(timestamp){
+ return comment_when(timestamp)
+ },
+ is_user_registered(){
+ return hub.is_user_registered()
+ },
get_seller_profile_and_items() {
- hub.call(
- 'get_hub_seller_page_info',
- { company: this.seller_company }
- ).then(data => {
+ let post_data = {company: this.seller_company}
+ if (this.page_name == 'profile'){
+ this.seller_company = null;
+ this.hub_seller = hub.settings.hub_seller_name
+ post_data = {hub_seller: this.hub_seller}
+ }
+ hub.call('get_hub_seller_page_info', post_data)
+ .then(data => {
this.init = false;
this.profile = data.profile;
this.items = data.items;
+ this.item_container_heading = data.is_featured_item? "Features Items":"Popular Items";
+ this.hub_seller = this.items[0].hub_seller;
+ this.recent_seller_reviews = data.recent_seller_reviews;
+ this.seller_product_view_stats = data.seller_product_view_stats;
const profile = this.profile;
@@ -91,11 +158,41 @@
: __('No description')
}
];
+
+ setTimeout(() => this.init_seller_traffic_chart(), 1);
+
});
},
go_to_item_details_page(hub_item_name) {
frappe.set_route(`marketplace/item/${hub_item_name}`);
+ },
+ go_to_seller_items_page(hub_seller) {
+ frappe.set_route(`marketplace/seller/${hub_seller}/items`);
+ },
+ init_seller_traffic_chart() {
+ let lables = []
+ let tooltip_lables = {}
+ let datasets = [{name:"Product Views",chartType: 'line',values: []}]
+ this.seller_product_view_stats.map((stat) => {
+ lables.push(stat.date.substring(5));
+ tooltip_lables[stat.date.substring(5)] = new Date(stat.date).toDateString();
+ datasets[0].values.push(stat.view_count);
+ });
+ let data = {labels: lables, datasets:datasets, tooltip_lables:tooltip_lables}
+ this.seller_traffic_chart = new Chart( "#seller_traffic_chart", { // or DOM element
+ data: data,
+
+ title: "Daily Product Views",
+ type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage'
+ height: 300,
+ colors: ['purple', '#ffa3ef', 'light-blue'],
+
+ tooltipOptions: {
+ formatTooltipX: d => this.seller_traffic_chart.data.tooltip_lables[d],
+ formatTooltipY: d => d + ' Views',
+ }
+ });
}
}
}
diff --git a/erpnext/public/js/hub/pages/SellerItems.vue b/erpnext/public/js/hub/pages/SellerItems.vue
new file mode 100644
index 0000000..852fbae
--- /dev/null
+++ b/erpnext/public/js/hub/pages/SellerItems.vue
@@ -0,0 +1,57 @@
+<template>
+ <div
+ class="marketplace-page"
+ :data-page-name="page_name"
+ v-if="init || items.length"
+ >
+ <h5>{{ item_container_heading }}</h5>
+ <item-cards-container
+ :container_name="item_container_heading"
+ :items="items"
+ :item_id_fieldname="item_id_fieldname"
+ :on_click="go_to_item_details_page"
+ >
+ </item-cards-container>
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'seller-items-page',
+ data() {
+ return {
+ page_name: frappe.get_route()[1],
+ seller_company: frappe.get_route()[2],
+
+ init: true,
+ items:[],
+ item_id_fieldname: 'name',
+ };
+ },
+ created() {
+ this.get_seller_and_items();
+ },
+ computed: {
+ item_container_heading() {
+ return __('Items by ' + this.seller_company);
+ }
+ },
+ methods: {
+ get_seller_and_items() {
+ hub.call(
+ 'get_items',
+ { company: this.seller_company }
+ ).then(data => {
+ this.init = false;
+ this.items = data;
+ });
+ },
+
+ go_to_item_details_page(hub_item_name) {
+ frappe.set_route(`marketplace/item/${hub_item_name}`);
+ }
+ }
+}
+</script>
+
+<style scoped></style>
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 64085a8..389e25e 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -255,11 +255,16 @@
// get valid options for tree based on user permission & locals dict
let unscrub_option = frappe.model.unscrub(option);
let user_permission = frappe.defaults.get_user_permissions();
+ let options;
+
if(user_permission && user_permission[unscrub_option]) {
- return user_permission[unscrub_option].map(perm => perm.doc);
+ options = user_permission[unscrub_option].map(perm => perm.doc);
} else {
- return $.map(locals[`:${unscrub_option}`], function(c) { return c.name; }).sort();
+ options = $.map(locals[`:${unscrub_option}`], function(c) { return c.name; }).sort();
}
+
+ // filter unique values, as there may be multiple user permissions for any value
+ return options.filter((value, index, self) => self.indexOf(value) === index);
},
get_tree_default: function(option) {
// set default for a field based on user permission
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index a6d723a..c5498c7 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -345,13 +345,14 @@
if company and tds_account:
accounts = [dict(company=company, account=tds_account)]
- fiscal_year = get_fiscal_year(today(), company=accounts[0].get('company'))[0]
+ fiscal_year = get_fiscal_year(today(), company=company)[0]
docs = get_tds_details(accounts, fiscal_year)
for d in docs:
try:
doc = frappe.get_doc(d)
doc.flags.ignore_permissions = True
+ doc.flags.ignore_mandatory = True
doc.insert()
except frappe.DuplicateEntryError:
doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py
new file mode 100644
index 0000000..ef1d582
--- /dev/null
+++ b/erpnext/regional/italy/__init__.py
@@ -0,0 +1,79 @@
+# coding=utf-8
+
+fiscal_regimes = [
+ "RF01-Ordinario",
+ "RF02-Contribuenti minimi (art.1, c.96-117, L. 244/07)",
+ "RF04-Agricoltura e attività connesse e pesca (artt.34 e 34-bis, DPR 633/72)",
+ "RF05-Vendita sali e tabacchi (art.74, c.1, DPR. 633/72)",
+ "RF06-Commercio fiammiferi (art.74, c.1, DPR 633/72)",
+ "RF07-Editoria (art.74, c.1, DPR 633/72)",
+ "RF08-Gestione servizi telefonia pubblica (art.74, c.1, DPR 633/72)",
+ "RF09-Rivendita documenti di trasporto pubblico e di sosta (art.74, c.1, DPR 633/72)",
+ "RF10-Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al DPR 640/72 (art.74, c.6, DPR 633/72)",
+ "RF11-Agenzie viaggi e turismo (art.74-ter, DPR 633/72)",
+ "RF12-Agriturismo (art.5, c.2, L. 413/91)",
+ "RF13-Vendite a domicilio (art.25-bis, c.6, DPR 600/73)",
+ "RF14-Rivendita beni usati, oggetti d’arte, d’antiquariato o da collezione (art.36, DL 41/95)",
+ "RF15-Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art.40-bis, DL 41/95)",
+ "RF16-IVA per cassa P.A. (art.6, c.5, DPR 633/72)",
+ "RF17-IVA per cassa (art. 32-bis, DL 83/2012)",
+ "RF18-Altro",
+ "RF19-Regime forfettario (art.1, c.54-89, L. 190/2014)"
+]
+
+tax_exemption_reasons = [
+ "N1-Escluse ex art. 15",
+ "N2-Non Soggette",
+ "N3-Non Imponibili",
+ "N4-Esenti",
+ "N5-Regime del margine / IVA non esposta in fattura",
+ "N6-Inversione Contabile",
+ "N7-IVA assolta in altro stato UE"
+]
+
+mode_of_payment_codes = [
+ "MP01-Contanti",
+ "MP02-Assegno",
+ "MP03-Assegno circolare",
+ "MP04-Contanti presso Tesoreria",
+ "MP05-Bonifico",
+ "MP06-Vaglia cambiario",
+ "MP07-Bollettino bancario",
+ "MP08-Carta di pagamento",
+ "MP09-RID",
+ "MP10-RID utenze",
+ "MP11-RID veloce",
+ "MP12-RIBA",
+ "MP13-MAV",
+ "MP14-Quietanza erario",
+ "MP15-Giroconto su conti di contabilità speciale",
+ "MP16-Domiciliazione bancaria",
+ "MP17-Domiciliazione postale",
+ "MP18-Bollettino di c/c postale",
+ "MP19-SEPA Direct Debit",
+ "MP20-SEPA Direct Debit CORE",
+ "MP21-SEPA Direct Debit B2B",
+ "MP22-Trattenuta su somme già riscosse"
+]
+
+vat_collectability_options = [
+ "I-Immediata",
+ "D-Differita",
+ "S-Scissione dei Pagamenti"
+]
+
+state_codes = {'Siracusa': 'SR', 'Bologna': 'BO', 'Grosseto': 'GR', 'Caserta': 'CE', 'Alessandria': 'AL', 'Ancona': 'AN', 'Pavia': 'PV',
+ 'Benevento or Beneventum': 'BN', 'Modena': 'MO', 'Lodi': 'LO', 'Novara': 'NO', 'Avellino': 'AV', 'Verona': 'VR', 'Forli-Cesena': 'FC',
+ 'Caltanissetta': 'CL', 'Brescia': 'BS', 'Rieti': 'RI', 'Treviso': 'TV', 'Ogliastra': 'OG', 'Olbia-Tempio': 'OT', 'Bergamo': 'BG',
+ 'Napoli': 'NA', 'Campobasso': 'CB', 'Fermo': 'FM', 'Roma': 'RM', 'Lucca': 'LU', 'Rovigo': 'RO', 'Piacenza': 'PC', 'Monza and Brianza': 'MB',
+ 'La Spezia': 'SP', 'Pescara': 'PE', 'Vercelli': 'VC', 'Enna': 'EN', 'Nuoro': 'NU', 'Medio Campidano': 'MD', 'Trieste': 'TS', 'Aosta': 'AO',
+ 'Firenze': 'FI', 'Trapani': 'TP', 'Messina': 'ME', 'Teramo': 'TE', 'Udine': 'UD', 'Verbano-Cusio-Ossola': 'VB', 'Padua': 'PD',
+ 'Reggio Emilia': 'RE', 'Frosinone': 'FR', 'Taranto': 'TA', 'Catanzaro': 'CZ', 'Belluno': 'BL', 'Pordenone': 'PN', 'Viterbo': 'VT',
+ 'Gorizia': 'GO', 'Vatican City': 'SCV', 'Ferrara': 'FE', 'Chieti': 'CH', 'Crotone': 'KR', 'Foggia': 'FG', 'Perugia': 'PG', 'Bari': 'BA',
+ 'Massa-Carrara': 'MS', 'Pisa': 'PI', 'Latina': 'LT', 'Salerno': 'SA', 'Turin': 'TO', 'Lecco': 'LC', 'Lecce': 'LE', 'Pistoia': 'PT', 'Como': 'CO',
+ 'Barletta-Andria-Trani': 'BT', 'Mantua': 'MN', 'Ragusa': 'RG', 'Macerata': 'MC', 'Imperia': 'IM', 'Palermo': 'PA', 'Matera': 'MT', "L'Aquila": 'AQ',
+ 'Milano': 'MI', 'Catania': 'CT', 'Pesaro e Urbino': 'PU', 'Potenza': 'PZ', 'Republic of San Marino': 'RSM', 'Genoa': 'GE', 'Brindisi': 'BR',
+ 'Cagliari': 'CA', 'Siena': 'SI', 'Vibo Valentia': 'VV', 'Reggio Calabria': 'RC', 'Ascoli Piceno': 'AP', 'Carbonia-Iglesias': 'CI', 'Oristano': 'OR',
+ 'Asti': 'AT', 'Ravenna': 'RA', 'Vicenza': 'VI', 'Savona': 'SV', 'Biella': 'BI', 'Rimini': 'RN', 'Agrigento': 'AG', 'Prato': 'PO', 'Cuneo': 'CN',
+ 'Cosenza': 'CS', 'Livorno or Leghorn': 'LI', 'Sondrio': 'SO', 'Cremona': 'CR', 'Isernia': 'IS', 'Trento': 'TN', 'Terni': 'TR', 'Bolzano/Bozen': 'BZ',
+ 'Parma': 'PR', 'Varese': 'VA', 'Venezia': 'VE', 'Sassari': 'SS', 'Arezzo': 'AR'}
\ No newline at end of file
diff --git a/erpnext/regional/italy/e-invoice.xml b/erpnext/regional/italy/e-invoice.xml
new file mode 100644
index 0000000..c886ee9
--- /dev/null
+++ b/erpnext/regional/italy/e-invoice.xml
@@ -0,0 +1,216 @@
+{%- macro format_float(value) -%}
+{{ "%.2f" % value|abs }}
+{%- endmacro -%}
+
+{%- macro render_address(address) %}
+<Indirizzo>{{ address.address_line1 }}</Indirizzo>
+<CAP>{{ address.pincode }}</CAP>
+<Comune>{{ address.city }}</Comune>
+{%- if address.state_code %}
+<Provincia>{{ address.state_code }}</Provincia>
+{%- endif %}
+<Nazione>{{ address.country_code }}</Nazione>
+{%- endmacro %}
+
+{%- macro render_discount_or_margin(item) -%}
+{%- if item.discount_percentage > 0.0 or item.margin_type %}
+<ScontoMaggiorazione>
+ {%- if item.discount_percentage > 0.0 %}
+ <Tipo>SC</Tipo>
+ <Percentuale>{{ format_float(item.discount_percentage) }}</Percentuale>
+ {%- endif %}
+ {%- if item.margin_rate_or_amount > 0.0 -%}
+ <Tipo>MG</Tipo>
+ {%- if item.margin_type == "Percentage" -%}
+ <Percentuale>{{ format_float(item.margin_rate_or_amount) }}</Percentuale>
+ {%- elif item.margin_type == "Amount" -%}
+ <Importo>{{ format_float(item.margin_rate_or_amount) }}</Importo>
+ {%- endif -%}
+ {%- endif %}
+</ScontoMaggiorazione>
+{%- endif -%}
+{%- endmacro -%}
+
+<?xml version='1.0' encoding='UTF-8'?>
+<p:FatturaElettronica xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ versione="{{ doc.transmission_format_code }}"
+ xsi:schemaLocation="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd">
+ <FatturaElettronicaHeader>
+ <DatiTrasmissione>
+ <IdTrasmittente>
+ <IdPaese>{{ doc.company_address_data.country_code }}</IdPaese>
+ <IdCodice>{{ doc.company_fiscal_code or doc.company_tax_id | replace("IT","") }}</IdCodice>
+ </IdTrasmittente>
+ <ProgressivoInvio>{{ doc.progressive_number }}</ProgressivoInvio>
+ <FormatoTrasmissione>{{ doc.transmission_format_code }}</FormatoTrasmissione>
+ <CodiceDestinatario>{{ doc.customer_data.recipient_code }}</CodiceDestinatario>
+ {% if doc.company_data.phone or doc.company_data.email -%}
+ <ContattiTrasmittente>
+ {% if doc.company_data.phone -%}<Telefono>{{ doc.company_data.phone }}</Telefono>{%- endif %}
+ {% if doc.company_data.email -%}<Email>{{ doc.company_data.email }}</Email>{%- endif %}
+ </ContattiTrasmittente>
+ {% endif -%}
+ </DatiTrasmissione>
+ <CedentePrestatore>
+ <DatiAnagrafici>
+ <IdFiscaleIVA>
+ <IdPaese>{{ doc.company_address_data.country_code }}</IdPaese>
+ <IdCodice>{{ doc.company_tax_id | replace("IT","") }}</IdCodice>
+ </IdFiscaleIVA>
+ {%- if doc.company_fiscal_code %}
+ <CodiceFiscale>{{ doc.company_fiscal_code }}</CodiceFiscale>
+ {%- endif %}
+ <Anagrafica>
+ <Denominazione>{{ doc.company }}</Denominazione>
+ </Anagrafica>
+ <RegimeFiscale>{{ doc.company_fiscal_regime.split("-")[0] }}</RegimeFiscale>
+ </DatiAnagrafici>
+ <Sede>
+ {{ render_address(doc.company_address_data) }}
+ </Sede>
+ {%- if doc.company_data.registration_number %}
+ <IscrizioneREA>
+ <Ufficio>{{ doc.company_data.registrar_office_province }}</Ufficio>
+ <NumeroREA>{{ doc.company_data.registration_number }}</NumeroREA>
+ {%- if doc.company_data.share_capital_amount %}
+ <CapitaleSociale>{{ format_float(doc.company_data.share_capital_amount) }}</CapitaleSociale>
+ {%- endif %}
+ {%- if doc.company_data.no_of_members %}
+ <SocioUnico>{{ doc.company_data.no_of_members.split("-")[0] }}</SocioUnico>
+ {%- endif %}
+ {%- if doc.company_data.liquidation_state %}
+ <StatoLiquidazione>{{ doc.company_data.liquidation_state.split("-")[0] }}</StatoLiquidazione>
+ {%- endif %}
+ </IscrizioneREA>
+ {%- endif %}
+ </CedentePrestatore>
+ <CessionarioCommittente>
+ <DatiAnagrafici>
+ {%- if doc.customer_data.customer_type == "Individual" %}
+ <CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
+ <Anagrafica>
+ <Nome>{{ doc.customer_data.first_name }}</Nome>
+ <Cognome>{{ doc.customer_data.last_name }}</Cognome>
+ </Anagrafica>
+ {%- else %}
+ {%- if doc.customer_data.is_public_administration %}
+ <CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
+ {%- else %}
+ <IdFiscaleIVA>
+ <IdPaese>{{ doc.customer_address_data.country_code }}</IdPaese>
+ <IdCodice>{{ doc.tax_id | replace("IT","") }}</IdCodice>
+ </IdFiscaleIVA>
+ {%- endif %}
+ <Anagrafica>
+ <Denominazione>{{ doc.customer_name }}</Denominazione>
+ </Anagrafica>
+ {%- endif %}
+ </DatiAnagrafici>
+ {%- if doc.customer_address_data %}
+ <Sede>
+ {{ render_address(doc.customer_address_data) }}
+ </Sede>
+ {%- endif %}
+ </CessionarioCommittente>
+ </FatturaElettronicaHeader>
+ <FatturaElettronicaBody>
+ <DatiGenerali>
+ <DatiGeneraliDocumento>
+ <TipoDocumento>{{ doc.type_of_document }}</TipoDocumento>
+ <Divisa>EUR</Divisa>
+ <Data>{{ doc.posting_date }}</Data>
+ <Numero>{{ doc.unamended_name }}</Numero>
+ {%- if doc.stamp_duty %}
+ <DatiBollo>
+ <BolloVirtuale>SI</BolloVirtuale>
+ <ImportoBollo>{{ format_float(doc.stamp_duty) }}</ImportoBollo>
+ </DatiBollo>
+ {%- endif %}
+ <ImportoTotaleDocumento>{{ format_float(doc.grand_total) }}</ImportoTotaleDocumento>
+ <Causale>VENDITA</Causale>
+ </DatiGeneraliDocumento>
+ {%- if doc.po_no %}
+ <DatiOrdineAcquisto>
+ <IdDocumento>{{ doc.po_no }}</IdDocumento>
+ {%- if doc.po_date %}
+ <Data>{{ doc.po_date }}</Data>
+ {%- endif %}
+ </DatiOrdineAcquisto>
+ {%- endif %}
+ {%- if doc.is_return and doc.return_against_unamended %}
+ <DatiFattureCollegate>
+ <IdDocumento>{{ doc.return_against_unamended }}</IdDocumento>
+ </DatiFattureCollegate>
+ {%- endif %}
+ {%- if doc.shipping_address_data %}
+ <DatiTrasporto>
+ <IndirizzoResa>
+ {{ render_address(doc.shipping_address_data) }}
+ </IndirizzoResa>
+ </DatiTrasporto>
+ {%- endif %}
+ </DatiGenerali>
+ <DatiBeniServizi>
+ {%- for item in doc.e_invoice_items %}
+ <DettaglioLinee>
+ <NumeroLinea>{{ item.idx }}</NumeroLinea>
+ <CodiceArticolo>
+ <CodiceTipo>CODICE</CodiceTipo>
+ <CodiceValore>{{ item.item_code }}</CodiceValore>
+ </CodiceArticolo>
+ <Descrizione>{{ html2text(item.description or '') or item.item_name }}</Descrizione>
+ <Quantita>{{ format_float(item.qty) }}</Quantita>
+ <UnitaMisura>{{ item.stock_uom }}</UnitaMisura>
+ <PrezzoUnitario>{{ format_float(item.price_list_rate or item.rate) }}</PrezzoUnitario>
+ {{ render_discount_or_margin(item) }}
+ <PrezzoTotale>{{ format_float(item.amount) }}</PrezzoTotale>
+ <AliquotaIVA>{{ format_float(item.tax_rate) }}</AliquotaIVA>
+ {%- if item.tax_exemption_reason %}
+ <Natura>{{ item.tax_exemption_reason.split("-")[0] }}</Natura>
+ {%- endif %}
+ </DettaglioLinee>
+ {%- endfor %}
+ {%- for tax, data in doc.tax_data.items() %}
+ <DatiRiepilogo>
+ <AliquotaIVA>{{ format_float(tax|flt) }}</AliquotaIVA>
+ {%- if data.tax_exemption_reason %}
+ <Natura>{{ data.tax_exemption_reason.split("-")[0] }}</Natura>
+ {%- endif %}
+ <ImponibileImporto>{{ format_float(data.taxable_amount) }}</ImponibileImporto>
+ <Imposta>{{ format_float(data.tax_amount) }}</Imposta>
+ <EsigibilitaIVA>{{ doc.vat_collectability.split("-")[0] }}</EsigibilitaIVA>
+ {%- if data.tax_exemption_law %}
+ <RiferimentoNormativo>{{ data.tax_exemption_law }}</RiferimentoNormativo>
+ {%- endif %}
+ </DatiRiepilogo>
+ {%- endfor %}
+ </DatiBeniServizi>
+ {%- if doc.payment_schedule %}
+ <DatiPagamento>
+ {%- if payment_schedule|length > 1 %}
+ <CondizioniPagamento>TP01</CondizioniPagamento>
+ {%- else %}
+ <CondizioniPagamento>TP02</CondizioniPagamento>
+ {%- endif %}
+ {%- for payment_term in doc.payment_schedule %}
+ <DettaglioPagamento>
+ <ModalitaPagamento>{{ payment_term.mode_of_payment_code.split("-")[0] }}</ModalitaPagamento>
+ <DataScadenzaPagamento>{{ payment_term.due_date }}</DataScadenzaPagamento>
+ <ImportoPagamento>{{ format_float(payment_term.payment_amount) }}</ImportoPagamento>
+ <IstitutoFinanziario>{{ payment_term.bank_account_name }}</IstitutoFinanziario>
+ {%- if payment_term.bank_account_iban %}
+ <IBAN>{{ payment_term.bank_account_iban }}</IBAN>
+ <ABI>{{ payment_term.bank_account_iban[5:10] }}</ABI>
+ <CAB>{{ payment_term.bank_account_iban[10:15] }}</CAB>
+ {%- endif %}
+ {%- if payment_term.bank_account_swift_number %}
+ <BIC>{{ payment_term.bank_account_swift_number }}</BIC>
+ {%- endif %}
+ </DettaglioPagamento>
+ {%- endfor %}
+ </DatiPagamento>
+ {%- endif %}
+ </FatturaElettronicaBody>
+</p:FatturaElettronica>
diff --git a/erpnext/regional/italy/sales_invoice.js b/erpnext/regional/italy/sales_invoice.js
new file mode 100644
index 0000000..3457f71
--- /dev/null
+++ b/erpnext/regional/italy/sales_invoice.js
@@ -0,0 +1,19 @@
+erpnext.setup_e_invoice_button = (doctype) => {
+ frappe.ui.form.on(doctype, {
+ refresh: (frm) => {
+ if(frm.doc.docstatus == 1) {
+ frm.add_custom_button('Generate E-Invoice', () => {
+ var w = window.open(
+ frappe.urllib.get_full_url(
+ "/api/method/erpnext.regional.italy.utils.generate_single_invoice?"
+ + "docname=" + frm.doc.name
+ )
+ )
+ if (!w) {
+ frappe.msgprint(__("Please enable pop-ups")); return;
+ }
+ });
+ }
+ }
+ });
+};
diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py
new file mode 100644
index 0000000..2b6e3af
--- /dev/null
+++ b/erpnext/regional/italy/setup.py
@@ -0,0 +1,161 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+# coding=utf-8
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options
+
+def setup(company=None, patch=True):
+ make_custom_fields()
+ setup_report()
+
+def make_custom_fields(update=True):
+ invoice_item_fields = [
+ dict(fieldname='tax_rate', label='Tax Rate',
+ fieldtype='Float', insert_after='description',
+ print_hide=1, hidden=1, read_only=1),
+ dict(fieldname='tax_amount', label='Tax Amount',
+ fieldtype='Currency', insert_after='tax_rate',
+ print_hide=1, hidden=1, read_only=1, options="currency"),
+ dict(fieldname='total_amount', label='Total Amount',
+ fieldtype='Currency', insert_after='tax_amount',
+ print_hide=1, hidden=1, read_only=1, options="currency")
+ ]
+
+ custom_fields = {
+ 'Company': [
+ dict(fieldname='sb_e_invoicing', label='E-Invoicing',
+ fieldtype='Section Break', insert_after='date_of_establishment', print_hide=1),
+ dict(fieldname='fiscal_regime', label='Fiscal Regime',
+ fieldtype='Select', insert_after='sb_e_invoicing', print_hide=1,
+ options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), fiscal_regimes))),
+ dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='fiscal_regime', print_hide=1,
+ description=_("Applicable if the company is an Individual or a Proprietorship")),
+ dict(fieldname='vat_collectability', label='VAT Collectability',
+ fieldtype='Select', insert_after='fiscal_code', print_hide=1,
+ options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), vat_collectability_options))),
+ dict(fieldname='cb_e_invoicing1', fieldtype='Column Break', insert_after='vat_collectability', print_hide=1),
+ dict(fieldname='registrar_office_province', label='Province of the Registrar Office',
+ fieldtype='Data', insert_after='cb_e_invoicing1', print_hide=1, length=2),
+ dict(fieldname='registration_number', label='Registration Number',
+ fieldtype='Data', insert_after='registrar_office_province', print_hide=1, length=20),
+ dict(fieldname='share_capital_amount', label='Share Capital',
+ fieldtype='Currency', insert_after='registration_number', print_hide=1,
+ description=_('Applicable if the company is SpA, SApA or SRL')),
+ dict(fieldname='no_of_members', label='No of Members',
+ fieldtype='Select', insert_after='share_capital_amount', print_hide=1,
+ options="\nSU-Socio Unico\nSM-Piu Soci", description=_("Applicable if the company is a limited liability company")),
+ dict(fieldname='liquidation_state', label='Liquidation State',
+ fieldtype='Select', insert_after='no_of_members', print_hide=1,
+ options="\nLS-In Liquidazione\nLN-Non in Liquidazione")
+ ],
+ 'Sales Taxes and Charges': [
+ dict(fieldname='tax_exemption_reason', label='Tax Exemption Reason',
+ fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
+ depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0',
+ options="\n" + "\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), tax_exemption_reasons))),
+ dict(fieldname='tax_exemption_law', label='Tax Exempt Under',
+ fieldtype='Text', insert_after='tax_exemption_reason', print_hide=1,
+ depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0')
+ ],
+ 'Customer': [
+ dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='tax_id', print_hide=1),
+ dict(fieldname='recipient_code', label='Recipient Code',
+ fieldtype='Data', insert_after='fiscal_code', print_hide=1, default="0000000"),
+ dict(fieldname='pec', label='Recipient PEC',
+ fieldtype='Data', insert_after='fiscal_code', print_hide=1),
+ dict(fieldname='is_public_administration', label='Is Public Administration',
+ fieldtype='Check', insert_after='is_internal_customer', print_hide=1,
+ description=_("Set this if the customer is a Public Administration company."),
+ depends_on='eval:doc.customer_type=="Company"'),
+ dict(fieldname='first_name', label='First Name', fieldtype='Data',
+ insert_after='salutation', print_hide=1, depends_on='eval:doc.customer_type!="Company"'),
+ dict(fieldname='last_name', label='Last Name', fieldtype='Data',
+ insert_after='first_name', print_hide=1, depends_on='eval:doc.customer_type!="Company"')
+ ],
+ 'Mode of Payment': [
+ dict(fieldname='mode_of_payment_code', label='Code',
+ fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
+ options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), mode_of_payment_codes)))
+ ],
+ 'Payment Schedule': [
+ dict(fieldname='mode_of_payment_code', label='Code',
+ fieldtype='Select', insert_after='mode_of_payment', print_hide=1,
+ options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), mode_of_payment_codes)),
+ fetch_from="mode_of_payment.mode_of_payment_code", read_only=1),
+ dict(fieldname='bank_account', label='Bank Account',
+ fieldtype='Link', insert_after='mode_of_payment_code', print_hide=1,
+ options="Bank Account"),
+ dict(fieldname='bank_account_name', label='Bank Name',
+ fieldtype='Data', insert_after='bank_account', print_hide=1,
+ fetch_from="bank_account.bank", read_only=1),
+ dict(fieldname='bank_account_no', label='Bank Account No',
+ fieldtype='Data', insert_after='bank_account_name', print_hide=1,
+ fetch_from="bank_account.bank_account_no", read_only=1),
+ dict(fieldname='bank_account_iban', label='IBAN',
+ fieldtype='Data', insert_after='bank_account_name', print_hide=1,
+ fetch_from="bank_account.iban", read_only=1),
+ dict(fieldname='bank_account_swift_number', label='Swift Code (BIC)',
+ fieldtype='Data', insert_after='bank_account_iban', print_hide=1,
+ fetch_from="bank_account.swift_number", read_only=1),
+ ],
+ "Sales Invoice": [
+ dict(fieldname='vat_collectability', label='VAT Collectability',
+ fieldtype='Select', insert_after='taxes_and_charges', print_hide=1,
+ options="\n".join(map(lambda x: frappe.safe_decode(x, encoding='utf-8'), vat_collectability_options)),
+ fetch_from="company.vat_collectability"),
+ dict(fieldname='sb_e_invoicing_reference', label='E-Invoicing',
+ fieldtype='Section Break', insert_after='pos_total_qty', print_hide=1),
+ dict(fieldname='company_tax_id', label='Company Tax ID',
+ fieldtype='Data', insert_after='sb_e_invoicing_reference', print_hide=1, read_only=1,
+ fetch_from="company.tax_id"),
+ dict(fieldname='company_fiscal_code', label='Company Fiscal Code',
+ fieldtype='Data', insert_after='company_tax_id', print_hide=1, read_only=1,
+ fetch_from="company.fiscal_code"),
+ dict(fieldname='company_fiscal_regime', label='Company Fiscal Regime',
+ fieldtype='Data', insert_after='company_fiscal_code', print_hide=1, read_only=1,
+ fetch_from="company.fiscal_regime"),
+ dict(fieldname='cb_e_invoicing_reference', fieldtype='Column Break',
+ insert_after='company_fiscal_regime', print_hide=1),
+ dict(fieldname='customer_fiscal_code', label='Customer Fiscal Code',
+ fieldtype='Data', insert_after='cb_e_invoicing_reference', read_only=1,
+ fetch_from="customer.fiscal_code"),
+ ],
+ 'Purchase Invoice Item': invoice_item_fields,
+ 'Sales Order Item': invoice_item_fields,
+ 'Delivery Note Item': invoice_item_fields,
+ 'Sales Invoice Item': invoice_item_fields,
+ 'Quotation Item': invoice_item_fields,
+ 'Purchase Order Item': invoice_item_fields,
+ 'Purchase Receipt Item': invoice_item_fields,
+ 'Supplier Quotation Item': invoice_item_fields,
+ 'Address': [
+ dict(fieldname='country_code', label='Country Code',
+ fieldtype='Data', insert_after='country', print_hide=1, read_only=0,
+ fetch_from="country.code"),
+ dict(fieldname='state_code', label='State Code',
+ fieldtype='Data', insert_after='state', print_hide=1)
+ ]
+ }
+
+ create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=update)
+
+def setup_report():
+ report_name = 'Electronic Invoice Register'
+
+ frappe.db.sql(""" update `tabReport` set disabled = 0 where
+ name = %s """, report_name)
+
+ if not frappe.db.get_value('Custom Role', dict(report=report_name)):
+ frappe.get_doc(dict(
+ doctype='Custom Role',
+ report=report_name,
+ roles= [
+ dict(role='Accounts User'),
+ dict(role='Accounts Manager')
+ ]
+ )).insert()
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
new file mode 100644
index 0000000..30280e2
--- /dev/null
+++ b/erpnext/regional/italy/utils.py
@@ -0,0 +1,346 @@
+from __future__ import unicode_literals
+
+import frappe, json, os
+from frappe.utils import flt, cstr
+from erpnext.controllers.taxes_and_totals import get_itemised_tax
+from frappe import _
+from frappe.utils.file_manager import save_file, remove_file
+from frappe.desk.form.load import get_attachments
+from erpnext.regional.italy import state_codes
+
+
+def update_itemised_tax_data(doc):
+ if not doc.taxes: return
+
+ itemised_tax = get_itemised_tax(doc.taxes)
+
+ for row in doc.items:
+ tax_rate = 0.0
+ if itemised_tax.get(row.item_code):
+ tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
+
+ row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+ row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
+ row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+
+@frappe.whitelist()
+def export_invoices(filters=None):
+ saved_xmls = []
+
+ invoices = frappe.get_all("Sales Invoice", filters=get_conditions(filters), fields=["*"])
+
+ for invoice in invoices:
+ attachments = get_e_invoice_attachments(invoice)
+ saved_xmls += [attachment.file_name for attachment in attachments]
+
+ zip_filename = "{0}-einvoices.zip".format(frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
+
+ download_zip(saved_xmls, zip_filename)
+
+
+@frappe.whitelist()
+def prepare_invoice(invoice, progressive_number):
+ #set company information
+ company = frappe.get_doc("Company", invoice.company)
+
+ invoice.progressive_number = progressive_number
+ invoice.unamended_name = get_unamended_name(invoice)
+ invoice.company_data = company
+ company_address = frappe.get_doc("Address", invoice.company_address)
+ invoice.company_address_data = company_address
+
+ #Set invoice type
+ if invoice.is_return and invoice.return_against:
+ invoice.type_of_document = "TD04" #Credit Note (Nota di Credito)
+ invoice.return_against_unamended = get_unamended_name(frappe.get_doc("Sales Invoice", invoice.return_against))
+ else:
+ invoice.type_of_document = "TD01" #Sales Invoice (Fattura)
+
+ #set customer information
+ invoice.customer_data = frappe.get_doc("Customer", invoice.customer)
+ customer_address = frappe.get_doc("Address", invoice.customer_address)
+ invoice.customer_address_data = customer_address
+
+ if invoice.shipping_address_name:
+ invoice.shipping_address_data = frappe.get_doc("Address", invoice.shipping_address_name)
+
+ if invoice.customer_data.is_public_administration:
+ invoice.transmission_format_code = "FPA12"
+ else:
+ invoice.transmission_format_code = "FPR12"
+
+ invoice.e_invoice_items = [item for item in invoice.items]
+ tax_data = get_invoice_summary(invoice.e_invoice_items, invoice.taxes)
+ invoice.tax_data = tax_data
+
+ #Check if stamp duty (Bollo) of 2 EUR exists.
+ stamp_duty_charge_row = next((tax for tax in invoice.taxes if tax.charge_type == _("Actual") and tax.tax_amount == 2.0 ), None)
+ if stamp_duty_charge_row:
+ invoice.stamp_duty = stamp_duty_charge_row.tax_amount
+
+ for item in invoice.e_invoice_items:
+ if item.tax_rate == 0.0 and item.tax_amount == 0.0:
+ item.tax_exemption_reason = tax_data["0.0"]["tax_exemption_reason"]
+
+ return invoice
+
+def get_conditions(filters):
+ filters = json.loads(filters)
+
+ conditions = {"docstatus": 1}
+
+ if filters.get("company"): conditions["company"] = filters["company"]
+ if filters.get("customer"): conditions["customer"] = filters["customer"]
+
+ if filters.get("from_date"): conditions["posting_date"] = (">=", filters["from_date"])
+ if filters.get("to_date"): conditions["posting_date"] = ("<=", filters["to_date"])
+
+ if filters.get("from_date") and filters.get("to_date"):
+ conditions["posting_date"] = ("between", [filters.get("from_date"), filters.get("to_date")])
+
+ return conditions
+
+#TODO: Use function from frappe once PR #6853 is merged.
+def download_zip(files, output_filename):
+ from zipfile import ZipFile
+
+ input_files = [frappe.get_site_path('private', 'files', filename) for filename in files]
+ output_path = frappe.get_site_path('private', 'files', output_filename)
+
+ with ZipFile(output_path, 'w') as output_zip:
+ for input_file in input_files:
+ output_zip.write(input_file, arcname=os.path.basename(input_file))
+
+ with open(output_path, 'rb') as fileobj:
+ filedata = fileobj.read()
+
+ frappe.local.response.filename = output_filename
+ frappe.local.response.filecontent = filedata
+ frappe.local.response.type = "download"
+
+def get_invoice_summary(items, taxes):
+ summary_data = frappe._dict()
+ for tax in taxes:
+ #Include only VAT charges.
+ if tax.charge_type == "Actual":
+ continue
+
+ #Charges to appear as items in the e-invoice.
+ if tax.charge_type in ["On Previous Row Total", "On Previous Row Amount"]:
+ reference_row = next((row for row in taxes if row.idx == int(tax.row_id or 0)), None)
+ if reference_row:
+ items.append(
+ frappe._dict(
+ idx=len(items)+1,
+ item_code=reference_row.description,
+ item_name=reference_row.description,
+ rate=reference_row.tax_amount,
+ qty=1.0,
+ amount=reference_row.tax_amount,
+ stock_uom=frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos"),
+ tax_rate=tax.rate,
+ tax_amount=(reference_row.tax_amount * tax.rate) / 100,
+ net_amount=reference_row.tax_amount,
+ taxable_amount=reference_row.tax_amount,
+ item_tax_rate="{}",
+ charges=True
+ )
+ )
+
+ #Check item tax rates if tax rate is zero.
+ if tax.rate == 0:
+ for item in items:
+ item_tax_rate = json.loads(item.item_tax_rate)
+ if tax.account_head in item_tax_rate:
+ key = cstr(item_tax_rate[tax.account_head])
+ summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0, "tax_exemption_reason": "", "tax_exemption_law": ""})
+ summary_data[key]["tax_amount"] += item.tax_amount
+ summary_data[key]["taxable_amount"] += item.net_amount
+ if key == "0.0":
+ summary_data[key]["tax_exemption_reason"] = tax.tax_exemption_reason
+ summary_data[key]["tax_exemption_law"] = tax.tax_exemption_law
+
+ if summary_data == {}: #Implies that Zero VAT has not been set on any item.
+ summary_data.setdefault("0.0", {"tax_amount": 0.0, "taxable_amount": tax.total,
+ "tax_exemption_reason": tax.tax_exemption_reason, "tax_exemption_law": tax.tax_exemption_law})
+
+ else:
+ item_wise_tax_detail = json.loads(tax.item_wise_tax_detail)
+ for rate_item in [tax_item for tax_item in item_wise_tax_detail.items() if tax_item[1][0] == tax.rate]:
+ key = cstr(tax.rate)
+ if not summary_data.get(key): summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0})
+ summary_data[key]["tax_amount"] += rate_item[1][1]
+ summary_data[key]["taxable_amount"] += sum([item.net_amount for item in items if item.item_code == rate_item[0]])
+
+ for item in items:
+ key = cstr(tax.rate)
+ if item.get("charges"):
+ if not summary_data.get(key): summary_data.setdefault(key, {"taxable_amount": 0.0})
+ summary_data[key]["taxable_amount"] += item.taxable_amount
+
+ return summary_data
+
+#Preflight for successful e-invoice export.
+def sales_invoice_validate(doc):
+ #Validate company
+ if doc.doctype != 'Sales Invoice':
+ return
+
+ if not doc.company_address:
+ frappe.throw(_("Please set an Address on the Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+ else:
+ validate_address(doc.company_address)
+
+ company_fiscal_regime = frappe.get_cached_value("Company", doc.company, 'fiscal_regime')
+ if not company_fiscal_regime:
+ frappe.throw(_("Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}")
+ .format(doc.company))
+ else:
+ doc.company_fiscal_regime = company_fiscal_regime
+
+ if not doc.company_tax_id and not doc.company_fiscal_code:
+ frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+
+ #Validate customer details
+ customer_type, is_public_administration = frappe.db.get_value("Customer", doc.customer, ["customer_type", "is_public_administration"])
+ if customer_type == _("Individual"):
+ if not doc.customer_fiscal_code:
+ frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+ else:
+ if is_public_administration:
+ if not doc.customer_fiscal_code:
+ frappe.throw(_("Please set Fiscal Code for the public administration '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+ else:
+ if not doc.tax_id:
+ frappe.throw(_("Please set Tax ID for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+
+ if not doc.customer_address:
+ frappe.throw(_("Please set the Customer Address"), title=_("E-Invoicing Information Missing"))
+ else:
+ validate_address(doc.customer_address)
+
+ if not len(doc.taxes):
+ frappe.throw(_("Please set at least one row in the Taxes and Charges Table"), title=_("E-Invoicing Information Missing"))
+ else:
+ for row in doc.taxes:
+ if row.rate == 0 and row.tax_amount == 0 and not row.tax_exemption_reason:
+ frappe.throw(_("Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges".format(row.idx)),
+ title=_("E-Invoicing Information Missing"))
+
+ for schedule in doc.payment_schedule:
+ if schedule.mode_of_payment and not schedule.mode_of_payment_code:
+ schedule.mode_of_payment_code = frappe.get_cached_value('Mode of Payment',
+ schedule.mode_of_payment, 'mode_of_payment_code')
+
+#Ensure payment details are valid for e-invoice.
+def sales_invoice_on_submit(doc, method):
+ #Validate payment details
+ if get_company_country(doc.company) not in ['Italy',
+ 'Italia', 'Italian Republic', 'Repubblica Italiana']:
+ return
+
+ if not len(doc.payment_schedule):
+ frappe.throw(_("Please set the Payment Schedule"), title=_("E-Invoicing Information Missing"))
+ else:
+ for schedule in doc.payment_schedule:
+ if not schedule.mode_of_payment:
+ frappe.throw(_("Row {0}: Please set the Mode of Payment in Payment Schedule".format(schedule.idx)),
+ title=_("E-Invoicing Information Missing"))
+ elif not frappe.db.get_value("Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"):
+ frappe.throw(_("Row {0}: Please set the correct code on Mode of Payment {1}".format(schedule.idx, schedule.mode_of_payment)),
+ title=_("E-Invoicing Information Missing"))
+
+ prepare_and_attach_invoice(doc)
+
+def prepare_and_attach_invoice(doc, replace=False):
+ progressive_name, progressive_number = get_progressive_name_and_number(doc, replace)
+
+ invoice = prepare_invoice(doc, progressive_number)
+ invoice_xml = frappe.render_template('erpnext/regional/italy/e-invoice.xml', context={"doc": invoice}, is_path=True)
+ invoice_xml = invoice_xml.replace("&", "&")
+
+ xml_filename = progressive_name + ".xml"
+ return save_file(xml_filename, invoice_xml, dt=doc.doctype, dn=doc.name, is_private=True)
+
+@frappe.whitelist()
+def generate_single_invoice(docname):
+ doc = frappe.get_doc("Sales Invoice", docname)
+
+ e_invoice = prepare_and_attach_invoice(doc, True)
+
+ content = None
+ with open(frappe.get_site_path('private', 'files', e_invoice.file_name), "r") as f:
+ content = f.read()
+
+ frappe.local.response.filename = e_invoice.file_name
+ frappe.local.response.filecontent = content
+ frappe.local.response.type = "download"
+
+#Delete e-invoice attachment on cancel.
+def sales_invoice_on_cancel(doc, method):
+ if get_company_country(doc.company) not in ['Italy',
+ 'Italia', 'Italian Republic', 'Repubblica Italiana']:
+ return
+
+ for attachment in get_e_invoice_attachments(doc):
+ remove_file(attachment.name, attached_to_doctype=doc.doctype, attached_to_name=doc.name)
+
+def get_company_country(company):
+ return frappe.get_cached_value('Company', company, 'country')
+
+def get_e_invoice_attachments(invoice):
+ out = []
+ attachments = get_attachments(invoice.doctype, invoice.name)
+ company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id
+
+ for attachment in attachments:
+ if attachment.file_name and attachment.file_name.startswith(company_tax_id) and attachment.file_name.endswith(".xml"):
+ out.append(attachment)
+
+ return out
+
+def validate_address(address_name):
+ fields = ["pincode", "city", "country_code"]
+ data = frappe.get_cached_value("Address", address_name, fields, as_dict=1) or {}
+
+ for field in fields:
+ if not data.get(field):
+ frappe.throw(_("Please set {0} for address {1}".format(field.replace('-',''), address_name)),
+ title=_("E-Invoicing Information Missing"))
+
+def get_unamended_name(doc):
+ attributes = ["naming_series", "amended_from"]
+ for attribute in attributes:
+ if not hasattr(doc, attribute):
+ return doc.name
+
+ if doc.amended_from:
+ return "-".join(doc.name.split("-")[:-1])
+ else:
+ return doc.name
+
+def get_progressive_name_and_number(doc, replace=False):
+ if replace:
+ for attachment in get_e_invoice_attachments(doc):
+ remove_file(attachment.name, attached_to_doctype=doc.doctype, attached_to_name=doc.name)
+ filename = attachment.file_name.split(".xml")[0]
+ return filename, filename.split("_")[1]
+
+ company_tax_id = doc.company_tax_id if doc.company_tax_id.startswith("IT") else "IT" + doc.company_tax_id
+ progressive_name = frappe.model.naming.make_autoname(company_tax_id + "_.#####")
+ progressive_number = progressive_name.split("_")[1]
+
+ return progressive_name, progressive_number
+
+def set_state_code(doc, method):
+ if doc.get('country_code'):
+ doc.country_code = doc.country_code.upper()
+
+ if not doc.get('state'):
+ return
+
+ if not (hasattr(doc, "state_code") and doc.country in ["Italy", "Italia", "Italian Republic", "Repubblica Italiana"]):
+ return
+
+ state_codes_lower = {key.lower():value for key,value in state_codes.items()}
+ doc.state_code = state_codes_lower.get(doc.get('state','').lower())
diff --git a/erpnext/regional/report/electronic_invoice_register/__init__.py b/erpnext/regional/report/electronic_invoice_register/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/electronic_invoice_register/__init__.py
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
new file mode 100644
index 0000000..67297f7
--- /dev/null
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Electronic Invoice Register"] = {
+ "filters": [
+ {
+ "fieldname":"from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ "width": "80"
+ },
+ {
+ "fieldname":"to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today()
+ },
+ {
+ "fieldname":"customer",
+ "label": __("Customer"),
+ "fieldtype": "Link",
+ "options": "Customer"
+ },
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ ],
+ "onload": function(reportview) {
+ reportview.page.add_inner_button(__("Export E-Invoices"), function() {
+ //TODO: refactor condition to disallow export if report has no data.
+ if (!reportview.data.length) {
+ frappe.msgprint(__("No data to export"));
+ return
+ }
+
+ var w = window.open(
+ frappe.urllib.get_full_url(
+ "/api/method/erpnext.regional.italy.utils.export_invoices?"
+ + "filters=" + JSON.stringify(reportview.get_filter_values())
+ )
+ );
+ if (!w) {
+ frappe.msgprint(__("Please enable pop-ups")); return;
+ }
+ })
+ }
+}
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.json b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.json
new file mode 100644
index 0000000..5470c49
--- /dev/null
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.json
@@ -0,0 +1,21 @@
+{
+ "add_total_row": 0,
+ "color": "green",
+ "creation": "2019-01-13 17:43:21.903589",
+ "disabled": 1,
+ "docstatus": 0,
+ "doctype": "Report",
+ "icon": "fa fa-file-code-o",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2019-01-13 19:03:56.187748",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Electronic Invoice Register",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Invoice",
+ "report_name": "Electronic Invoice Register",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
new file mode 100644
index 0000000..376ba3e
--- /dev/null
+++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from erpnext.accounts.report.sales_register.sales_register import _execute
+
+def execute(filters=None):
+ return _execute(filters)
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 3a8b0df..24cb405 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -762,7 +762,7 @@
"no_copy": 0,
"options": "currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -1914,7 +1914,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2019-01-07 16:51:56.248107",
+ "modified": "2019-02-18 18:57:25.277633",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 19065df..5ca2f81 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -961,7 +961,7 @@
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -2477,7 +2477,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2019-01-07 16:51:51.852343",
+ "modified": "2019-02-18 18:53:23.425126",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 8b4d684..c54430f 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -166,7 +166,7 @@
});
frappe.ui.form.on('Sales Invoice', 'selling_price_list', (frm) => {
- if(this.items) {
+ if(this.items && frm.doc.pos_profile) {
this.items.reset_items();
}
})
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json
index 5c95f28..bd82302 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.json
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-09-21 12:46:29.451048",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-09-21 12:46:29.451048",
+ "modified": "2019-02-12 14:30:40.043652",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Analytics",
diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
index 67051c8..b236151 100644
--- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
+++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js
@@ -33,7 +33,8 @@
label: __("Company"),
fieldtype: "Link",
options: "Company",
- default: frappe.defaults.get_user_default("Company")
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
},
{
fieldname:"item_group",
diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
index db11ae9..41c7f76 100644
--- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
+++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
@@ -15,7 +15,7 @@
item_details = get_item_details()
data = []
- company_currency = get_company_currency(filters["company"])
+ company_currency = get_company_currency(filters.get("company"))
for d in entries:
if d.stock_qty > 0 or filters.get('show_return_entries', 0):
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index c3260ab..1b08a22 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -46,7 +46,7 @@
def test_coa_based_on_country_template(self):
countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France",
- "Guatemala", "Indonesia", "Mexico", "Nicaragua", "Netherlands", "Singapore",
+ "Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore",
"Brazil", "Argentina", "Hungary", "Taiwan"]
for country in countries:
diff --git a/erpnext/setup/doctype/setup_progress/setup_progress.py b/erpnext/setup/doctype/setup_progress/setup_progress.py
index 9187eb7..e1402f5 100644
--- a/erpnext/setup/doctype/setup_progress/setup_progress.py
+++ b/erpnext/setup/doctype/setup_progress/setup_progress.py
@@ -16,8 +16,9 @@
return frappe.local.setup_progress
def get_action_completed_state(action_name):
- return [d.is_completed for d in get_setup_progress().actions
- if d.action_name == action_name][0]
+ for d in get_setup_progress().actions:
+ if d.action_name == action_name:
+ return d.is_completed
def update_action_completed_state(action_name):
action_table_doc = [d for d in get_setup_progress().actions
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index 62c9e7b..8a164f9 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.py
@@ -33,7 +33,7 @@
tabCompany limit 1""") and 'Yes' or 'No'
bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms,
- default_letter_head, default_bank_account, enable_perpetual_inventory from `tabCompany`""",
+ default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""",
as_dict=1, update={"doctype":":Company"})
party_account_types = frappe.db.sql(""" select name, ifnull(account_type, '') from `tabParty Type`""")
diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py
index 6242fa7..487c765 100644
--- a/erpnext/stock/dashboard/item_dashboard.py
+++ b/erpnext/stock/dashboard/item_dashboard.py
@@ -28,7 +28,7 @@
# user does not have access on warehouse
return []
- return frappe.db.get_all('Bin', fields=['item_code', 'warehouse', 'projected_qty',
+ items = frappe.db.get_all('Bin', fields=['item_code', 'warehouse', 'projected_qty',
'reserved_qty', 'reserved_qty_for_production', 'reserved_qty_for_sub_contract', 'actual_qty', 'valuation_rate'],
or_filters={
'projected_qty': ['!=', 0],
@@ -41,3 +41,10 @@
order_by=sort_by + ' ' + sort_order,
limit_start=start,
limit_page_length='21')
+
+ for item in items:
+ item.update({
+ 'item_name': frappe.get_cached_value("Item", item.item_code, 'item_name')
+ })
+
+ return items
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 50996ed..1eb2b09 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -391,7 +391,7 @@
return invoiced_qty_map
-def get_returned_qty_map(sales_orders):
+def get_returned_qty_map_against_so(sales_orders):
"""returns a map: {so_detail: returned_qty}"""
returned_qty_map = {}
@@ -403,12 +403,25 @@
return returned_qty_map
+def get_returned_qty_map_against_dn(delivery_note):
+ """returns a map: {so_detail: returned_qty}"""
+ returned_qty_map = frappe._dict(frappe.db.sql("""select dn_item.item_code, sum(abs(dn_item.qty)) as qty
+ from `tabDelivery Note Item` dn_item, `tabDelivery Note` dn
+ where dn.name = dn_item.parent
+ and dn.docstatus = 1
+ and dn.is_return = 1
+ and dn.return_against = %s
+ group by dn_item.item_code
+ """, delivery_note))
+
+ return returned_qty_map
+
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
doc = frappe.get_doc('Delivery Note', source_name)
sales_orders = [d.against_sales_order for d in doc.items]
- returned_qty_map = get_returned_qty_map(sales_orders)
-
+ returned_qty_map_against_so = get_returned_qty_map_against_so(sales_orders)
+ returned_qty_map_against_dn = get_returned_qty_map_against_dn(source_name)
invoiced_qty_map = get_invoiced_qty_map(source_name)
def set_missing_values(source, target):
@@ -428,13 +441,26 @@
target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
def update_item(source_doc, target_doc, source_parent):
- target_doc.qty = (source_doc.qty -
- invoiced_qty_map.get(source_doc.name, 0) - returned_qty_map.get(source_doc.so_detail, 0))
+ target_doc.qty, returned_qty = get_pending_qty(source_doc)
+ if not source_doc.so_detail:
+ returned_qty_map_against_dn[source_doc.item_code] = returned_qty
if source_doc.serial_no and source_parent.per_billed > 0:
target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code,
target_doc.qty, source_parent.name)
+ def get_pending_qty(item_row):
+ pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) - returned_qty_map_against_so.get(item_row.so_detail, 0)
+ returned_qty = flt(returned_qty_map_against_dn.get(item_row.item_code, 0))
+ if not item_row.so_detail:
+ if returned_qty >= pending_qty:
+ pending_qty = 0
+ returned_qty -= pending_qty
+ else:
+ pending_qty -= returned_qty
+ returned_qty = 0
+ return pending_qty, returned_qty
+
doc = get_mapped_doc("Delivery Note", source_name, {
"Delivery Note": {
"doctype": "Sales Invoice",
@@ -453,7 +479,7 @@
"cost_center": "cost_center"
},
"postprocess": update_item,
- "filter": lambda d: abs(d.qty) - abs(invoiced_qty_map.get(d.name, 0))<=0
+ "filter": lambda d: get_pending_qty(d)[0]<=0
},
"Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges",
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 0c5a71c..5c03121 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -636,7 +636,7 @@
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, company)
-
+
def test_make_sales_invoice_from_dn_for_returned_qty(self):
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
@@ -655,6 +655,33 @@
si = make_sales_invoice(dn.name)
self.assertEquals(si.items[0].qty, 1)
+ def test_make_sales_invoice_from_dn_with_returned_qty_against_dn(self):
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ dn = create_delivery_note(qty=8, do_not_submit=True)
+ dn.append("items", {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": 1,
+ "rate": 100,
+ "conversion_factor": 1.0,
+ "expense_account": "Cost of Goods Sold - _TC",
+ "cost_center": "_Test Cost Center - _TC"
+ })
+ dn.submit()
+
+ si1 = make_sales_invoice(dn.name)
+ si1.items[0].qty = 4
+ si1.items.pop(1)
+ si1.save()
+ si1.submit()
+
+ create_delivery_note(is_return=1, return_against=dn.name, qty=-2)
+
+ si2 = make_sales_invoice(dn.name)
+ self.assertEquals(si2.items[0].qty, 2)
+ self.assertEquals(si2.items[1].qty, 1)
+
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 9fe741a..1e73d68 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -793,7 +793,7 @@
"no_copy": 0,
"options": "currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -928,7 +928,7 @@
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
- "precision": "2",
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -2310,7 +2310,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2019-01-07 16:51:53.322875",
+ "modified": "2019-02-18 18:58:51.342901",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 81997ed..58c9077 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -38,6 +38,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-flag",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -70,6 +71,7 @@
"no_copy": 0,
"options": "STO-ITEM-.YYYY.-",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -104,6 +106,7 @@
"oldfieldname": "item_code",
"oldfieldtype": "Data",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -172,6 +175,7 @@
"oldfieldname": "item_name",
"oldfieldtype": "Data",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -190,37 +194,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "barcode",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Barcode",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "",
"fieldname": "item_group",
"fieldtype": "Link",
@@ -238,6 +211,7 @@
"oldfieldtype": "Link",
"options": "Item Group",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -305,6 +279,7 @@
"oldfieldtype": "Link",
"options": "UOM",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -335,6 +310,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -435,6 +411,7 @@
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -701,6 +678,7 @@
"oldfieldname": "tolerance",
"oldfieldtype": "Currency",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -800,6 +778,7 @@
"oldfieldtype": "Link",
"options": "Brand",
"permlevel": 0,
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -833,6 +812,7 @@
"oldfieldname": "description",
"oldfieldtype": "Text",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -933,6 +913,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-truck",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1000,6 +981,7 @@
"oldfieldname": "end_of_life",
"oldfieldtype": "Date",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1067,6 +1049,7 @@
"no_copy": 0,
"options": "\nFIFO\nMoving Average",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1099,6 +1082,7 @@
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1134,6 +1118,7 @@
"oldfieldname": "warranty_period",
"oldfieldtype": "Data",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1167,6 +1152,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1200,6 +1186,7 @@
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1234,6 +1221,7 @@
"no_copy": 0,
"options": "fa fa-rss",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1268,6 +1256,7 @@
"no_copy": 0,
"options": "Item Reorder",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1337,6 +1326,7 @@
"oldfieldtype": "Table",
"options": "UOM Conversion Detail",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1407,6 +1397,7 @@
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1643,6 +1634,7 @@
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1676,6 +1668,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1913,6 +1906,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2015,6 +2009,7 @@
"oldfieldname": "min_order_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2114,6 +2109,7 @@
"oldfieldname": "lead_time_days",
"oldfieldtype": "Int",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2148,6 +2144,7 @@
"oldfieldname": "last_purchase_rate",
"oldfieldtype": "Currency",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -2246,6 +2243,7 @@
"no_copy": 0,
"options": "Manufacturer",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2278,6 +2276,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2311,6 +2310,7 @@
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2345,6 +2345,7 @@
"no_copy": 0,
"options": "Item Supplier",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2507,6 +2508,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-tag",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2606,6 +2608,7 @@
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2641,6 +2644,7 @@
"oldfieldname": "max_discount",
"oldfieldtype": "Currency",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3031,6 +3035,7 @@
"no_copy": 0,
"options": "Item Customer Detail",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3064,6 +3069,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3099,6 +3105,7 @@
"oldfieldtype": "Table",
"options": "Item Tax",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3132,6 +3139,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-search",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3167,6 +3175,7 @@
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3267,6 +3276,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-cogs",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3302,6 +3312,7 @@
"oldfieldtype": "Link",
"options": "BOM",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -3338,6 +3349,7 @@
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3400,6 +3412,7 @@
"length": 0,
"no_copy": 1,
"permlevel": 0,
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3432,6 +3445,7 @@
"no_copy": 0,
"options": "fa fa-globe",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3464,6 +3478,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3563,6 +3578,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3597,6 +3613,7 @@
"no_copy": 0,
"options": "Website Slideshow",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3631,6 +3648,7 @@
"no_copy": 0,
"options": "",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3693,6 +3711,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3727,6 +3746,7 @@
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3761,6 +3781,7 @@
"no_copy": 0,
"options": "Website Item Group",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3794,6 +3815,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3826,6 +3848,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3859,6 +3882,7 @@
"no_copy": 0,
"options": "Item Website Specification",
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -3891,6 +3915,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -4114,10 +4139,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
- "modified": "2019-01-07 16:52:05.096907",
+ "modified": "2019-02-16 17:43:56.039611",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
+ "name_case": "",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 566b638..bd06688 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -700,15 +700,14 @@
frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
return
if self.has_variants:
- updated = []
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
- for d in variants:
- variant = frappe.get_doc("Item", d)
- copy_attributes_to_variant(self, variant)
- variant.save()
- updated.append(d.item_code)
- if updated:
- frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
+ if variants:
+ if len(variants) <= 30:
+ update_variants(variants, self, publish_progress=False)
+ frappe.msgprint(_("Item Variants updated"))
+ else:
+ frappe.enqueue("erpnext.stock.doctype.item.item.update_variants",
+ variants=variants, template=self, now=frappe.flags.in_test, timeout=600)
def validate_has_variants(self):
if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
@@ -997,3 +996,13 @@
return frappe.get_all("Item Attribute Value", fields = ["attribute_value"],
filters = {'parent': parent, 'attribute_value': ("like", "%%%s%%" % attribute_value)})
+
+def update_variants(variants, template, publish_progress=True):
+ count=0
+ for d in variants:
+ variant = frappe.get_doc("Item", d)
+ copy_attributes_to_variant(template, variant)
+ variant.save()
+ count+=1
+ if publish_progress:
+ frappe.publish_progress(count*100/len(variants), title = _("Updating Variants..."))
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 455dff4..3782f54 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -11,7 +11,7 @@
class TestItemPrice(unittest.TestCase):
def setUp(self):
- frappe.db.sql("delete from `tabItem Price`")
+ frappe.db.sql("delete from `tabItem Price`")
make_test_records_for_doctype("Item Price", force=True)
def test_duplicate_item(self):
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
index 678de1a..0422442 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -5,14 +5,17 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe import _
class ItemVariantSettings(Document):
+ invalid_fields_for_copy_fields_in_variants = ['barcodes']
+
def set_default_fields(self):
self.fields = []
fields = frappe.get_meta('Item').fields
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
"show_variant_in_website", "standard_rate", "opening_stock", "image", "description",
- "variant_of", "valuation_rate", "description",
+ "variant_of", "valuation_rate", "description", "barcodes",
"website_image", "thumbnail", "website_specifiations", "web_long_description"]
for d in fields:
@@ -20,4 +23,14 @@
d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only']:
self.append('fields', {
'field_name': d.fieldname
- })
\ No newline at end of file
+ })
+
+ def remove_invalid_fields_for_copy_fields_in_variants(self):
+ fields = [row for row in self.fields if row.field_name not in self.invalid_fields_for_copy_fields_in_variants]
+ self.fields = fields
+ self.save()
+
+ def validate(self):
+ for d in self.fields:
+ if d.field_name in self.invalid_fields_for_copy_fields_in_variants:
+ frappe.throw(_('Cannot set the field <b>{0}</b> for copying in variants').format(d.field_name))
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 2b0ed38..cb4afef 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -85,7 +85,7 @@
// stop
frm.add_custom_button(__('Stop'),
- () => frm.events.update_status(frm, 'Stop'));
+ () => frm.events.update_status(frm, 'Stopped'));
}
}
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index f3e3abf..47d61f8 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -81,9 +81,9 @@
def set_title(self):
'''Set title as comma separated list of items'''
- items = ', '.join([d.item_name for d in self.items][:4])
-
- self.title = _('{0} for {1}'.format(self.material_request_type, items))[:100]
+ if not self.title:
+ items = ', '.join([d.item_name for d in self.items][:3])
+ self.title = _('{0} Request for {1}').format(self.material_request_type, items)[:100]
def on_submit(self):
# frappe.db.set(self, 'status', 'Submitted')
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index f006d00..cb51e22 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -125,7 +125,7 @@
else:
self.status = "Completed"
-
+
# Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty, reserved_qty_for_subcontract in bin
# depends upon updated ordered qty in PO
@@ -311,7 +311,7 @@
"\n".join(warehouse_with_no_account))
return process_gl_map(gl_entries)
-
+
def get_asset_gl_entry(self, gl_entries):
for d in self.get("items"):
if d.is_fixed_asset:
@@ -405,6 +405,11 @@
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
+ doc = frappe.get_doc('Purchase Receipt', source_name)
+ purchase_orders = [d.purchase_order for d in doc.items]
+ returned_qty_map_against_po = get_returned_qty_map_against_po(purchase_orders)
+ returned_qty_map_against_pr = get_returned_qty_map_against_pr(source_name)
+
invoiced_qty_map = get_invoiced_qty_map(source_name)
def set_missing_values(source, target):
@@ -417,7 +422,23 @@
doc.run_method("calculate_taxes_and_totals")
def update_item(source_doc, target_doc, source_parent):
- target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0)
+ target_doc.qty, returned_qty = get_pending_qty(source_doc)
+ if not source_doc.purchase_order_item:
+ returned_qty_map_against_pr[source_doc.item_code] = returned_qty
+
+ def get_pending_qty(item_row):
+ pending_qty = item_row.qty - invoiced_qty_map.get(item_row.name, 0) \
+ - returned_qty_map_against_po.get(item_row.purchase_order_item, 0)
+ returned_qty = flt(returned_qty_map_against_pr.get(item_row.item_code, 0))
+ if not item_row.purchase_order_item:
+ if returned_qty >= pending_qty:
+ pending_qty = 0
+ returned_qty -= pending_qty
+ else:
+ pending_qty -= returned_qty
+ returned_qty = 0
+ return pending_qty, returned_qty
+
doclist = get_mapped_doc("Purchase Receipt", source_name, {
"Purchase Receipt": {
@@ -440,7 +461,7 @@
"asset": "asset",
},
"postprocess": update_item,
- "filter": lambda d: abs(d.qty) - abs(invoiced_qty_map.get(d.name, 0))<=0
+ "filter": lambda d: get_pending_qty(d)[0]<=0
},
"Purchase Taxes and Charges": {
"doctype": "Purchase Taxes and Charges",
@@ -462,6 +483,31 @@
return invoiced_qty_map
+def get_returned_qty_map_against_po(purchase_orders):
+ """returns a map: {so_detail: returned_qty}"""
+ returned_qty_map = {}
+
+ for name, returned_qty in frappe.get_all('Purchase Order Item', fields = ["name", "returned_qty"],
+ filters = {'parent': ('in', purchase_orders), 'docstatus': 1}, as_list=1):
+ if not returned_qty_map.get(name):
+ returned_qty_map[name] = 0
+ returned_qty_map[name] += returned_qty
+
+ return returned_qty_map
+
+def get_returned_qty_map_against_pr(purchase_receipt):
+ """returns a map: {so_detail: returned_qty}"""
+ returned_qty_map = frappe._dict(frappe.db.sql("""select pr_item.item_code, sum(abs(pr_item.qty)) as qty
+ from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
+ where pr.name = pr_item.parent
+ and pr.docstatus = 1
+ and pr.is_return = 1
+ and pr.return_against = %s
+ group by pr_item.item_code
+ """, purchase_receipt))
+
+ return returned_qty_map
+
@frappe.whitelist()
def make_purchase_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 472083b..c5ae838 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -404,6 +404,44 @@
set_perpetual_inventory(0, pr.company)
+ def test_make_purchase_invoice_from_pr_for_returned_qty(self):
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po
+
+ po = create_purchase_order()
+ pr = create_pr_against_po(po.name)
+
+ pr1 = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-1, do_not_submit=True)
+ pr1.items[0].purchase_order = po.name
+ pr1.items[0].purchase_order_item = po.items[0].name
+ pr1.submit()
+
+ pi = make_purchase_invoice(pr.name)
+ self.assertEquals(pi.items[0].qty, 3)
+
+ def test_make_purchase_invoice_from_dn_with_returned_qty_against_dn(self):
+ pr1 = make_purchase_receipt(qty=8, do_not_submit=True)
+ pr1.append("items", {
+ "item_code": "_Test Item",
+ "warehouse": "_Test Warehouse - _TC",
+ "qty": 1,
+ "received_qty": 1,
+ "rate": 100,
+ "conversion_factor": 1.0,
+ })
+ pr1.submit()
+
+ pi1 = make_purchase_invoice(pr1.name)
+ pi1.items[0].qty = 4
+ pi1.items.pop(1)
+ pi1.save()
+ pi1.submit()
+
+ make_purchase_receipt(is_return=1, return_against=pr1.name, qty=-2)
+
+ pi2 = make_purchase_invoice(pr1.name)
+ self.assertEquals(pi2.items[0].qty, 2)
+ self.assertEquals(pi2.items[1].qty, 1)
+
def get_gl_entries(voucher_type, voucher_no):
return frappe.db.sql("""select account, debit, credit, cost_center
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 3746dfb..528f799 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -417,7 +417,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Recd Quantity",
+ "label": "Received Quantity",
"length": 0,
"no_copy": 0,
"oldfieldname": "received_qty",
@@ -2608,7 +2608,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2019-01-07 16:51:59.002215",
+ "modified": "2019-02-18 16:04:36.138776",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 5d3c6c4..3a52530 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -293,8 +293,9 @@
total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
if total_completed_qty > flt(completed_qty):
- frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Time Logs")
- .format(d.idx, d.operation, total_completed_qty, self.work_order), OperationsNotCompleteError)
+ job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
+ frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Job Card # {4}")
+ .format(d.idx, d.operation, total_completed_qty, self.work_order, job_card), OperationsNotCompleteError)
def check_duplicate_entry_for_work_order(self):
other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", {
@@ -914,6 +915,11 @@
filters={'parent': self.work_order, 'item_code': item_code},
fields=["required_qty", "consumed_qty"]
)
+ if not req_items:
+ frappe.msgprint(_("Did not found transfered item {0} in Work Order {1}, the item not added in Stock Entry")
+ .format(item_code, self.work_order))
+ continue
+
req_qty = flt(req_items[0].required_qty)
req_qty_each = flt(req_qty / manufacturing_qty)
consumed_qty = flt(req_items[0].consumed_qty)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index eb60ce5..a00d279 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -259,6 +259,7 @@
def submit(self):
if len(self.items) > 100:
+ msgprint(_("The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage"))
self.queue_action('submit')
else:
self._submit()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index ec1031c..7f0e670 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -16,6 +16,9 @@
from six import string_types, iteritems
+sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
+purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
+
@frappe.whitelist()
def get_item_details(args):
"""
@@ -228,7 +231,7 @@
#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
if not args.uom:
- if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
+ if args.get('doctype') in sales_doctypes:
args.uom = item.sales_uom if item.sales_uom else item.stock_uom
elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \
(args.get('doctype') == 'Material Request' and args.get('material_request_type') == 'Purchase'):
@@ -281,14 +284,15 @@
out.conversion_factor = 1.0
else:
out.conversion_factor = args.conversion_factor or \
- get_conversion_factor(item.item_code, args.uom).get("conversion_factor")
+ get_conversion_factor(item.name, args.uom).get("conversion_factor")
args.conversion_factor = out.conversion_factor
out.stock_qty = out.qty * out.conversion_factor
# calculate last purchase rate
- from erpnext.buying.doctype.purchase_order.purchase_order import item_last_purchase_rate
- out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.item_code, out.conversion_factor)
+ if args.get('doctype') in purchase_doctypes:
+ from erpnext.buying.doctype.purchase_order.purchase_order import item_last_purchase_rate
+ out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.name, out.conversion_factor)
# if default specified in item is for another company, fetch from company
for d in [
@@ -486,7 +490,7 @@
price_list_rate = get_item_price(item_price_args, item_code)
if price_list_rate:
desired_qty = args.get("qty")
- if check_packing_list(price_list_rate[0][0], desired_qty, item_code):
+ if desired_qty and check_packing_list(price_list_rate[0][0], desired_qty, item_code):
item_price_data = price_list_rate
else:
for field in ["customer", "supplier", "min_qty"]:
@@ -517,12 +521,15 @@
:param qty: Derised Qt
"""
+ flag = True
item_price = frappe.get_doc("Item Price", price_list_rate_name)
- if desired_qty and item_price.packing_unit:
+ if item_price.packing_unit:
packing_increment = desired_qty % item_price.packing_unit
- if packing_increment == 0:
- return True
+ if packing_increment != 0:
+ flag = False
+
+ return flag
def validate_price_list(args):
if args.get("price_list"):
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.json b/erpnext/stock/report/stock_analytics/stock_analytics.json
index efd5e99..7a2ed2b 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.json
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.json
@@ -1,12 +1,13 @@
{
- "add_total_row": 0,
+ "add_total_row": 1,
"creation": "2018-10-08 12:11:32.133020",
+ "disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
- "modified": "2018-10-08 12:18:42.834270",
+ "modified": "2019-02-12 14:32:22.874082",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Analytics",