Merge pull request #19955 from deepeshgarg007/employee_advance_return
feat: Capture return amount against Employee Advance via Journal Entry
diff --git a/README.md b/README.md
index 64f8d67..ed57a17 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,26 @@
</div>
-Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB.
+ERPNext as a monolith includes the following areas for managing businesses:
-ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
+1. [Accounting](https://erpnext.com/docs/user/manual/en/accounts)
+1. [Inventory](https://erpnext.com/docs/user/manual/en/stock)
+1. [CRM](https://erpnext.com/docs/user/manual/en/CRM)
+1. [Sales](https://erpnext.com/docs/user/manual/en/selling)
+1. [Purchase](https://erpnext.com/docs/user/manual/en/buying)
+1. [HRMS](https://erpnext.com/docs/user/manual/en/human-resources)
+1. [Project Management](https://erpnext.com/docs/user/manual/en/projects)
+1. [Support](https://erpnext.com/docs/user/manual/en/support)
+1. [Asset Management](https://erpnext.com/docs/user/manual/en/asset)
+1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management)
+1. [Manufacturing](https://erpnext.com/docs/user/manual/en/manufacturing)
+1. [Website Management](https://erpnext.com/docs/user/manual/en/website)
+1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext)
+1. [And More](https://erpnext.com/docs/user/manual/en/)
+
+ERPNext requires MariaDB.
+
+ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a full-stack web app framework built with Python & JavaScript.
- [User Guide](https://erpnext.com/docs/user)
- [Discussion Forum](https://discuss.erpnext.com/)
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 078e058..041e419 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -29,7 +29,6 @@
self.validate_and_set_fiscal_year()
self.pl_must_have_cost_center()
self.validate_cost_center()
- self.validate_dimensions_for_pl_and_bs()
if not self.flags.from_repost:
self.check_pl_account()
@@ -39,6 +38,7 @@
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
if not from_repost:
self.validate_account_details(adv_adj)
+ self.validate_dimensions_for_pl_and_bs()
check_freezing_date(self.posting_date, adv_adj)
validate_frozen_account(self.account, adv_adj)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index e25942c..8897337 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -968,7 +968,7 @@
# The date used to retreive the exchange rate here is the date passed
# in as an argument to this function.
- elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
+ elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else:
exchange_rate = 1
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index adf47ed..2192b7b 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -652,14 +652,16 @@
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
) {
if(total_positive_outstanding > total_negative_outstanding)
- frm.set_value("paid_amount",
- total_positive_outstanding - total_negative_outstanding);
+ if (!frm.doc.paid_amount)
+ frm.set_value("paid_amount",
+ total_positive_outstanding - total_negative_outstanding);
} else if (
total_negative_outstanding &&
total_positive_outstanding < total_negative_outstanding
) {
- frm.set_value("received_amount",
- total_negative_outstanding - total_positive_outstanding);
+ if (!frm.doc.received_amount)
+ frm.set_value("received_amount",
+ total_negative_outstanding - total_positive_outstanding);
}
}
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index 971d308..29d8378 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -389,8 +389,7 @@
"fieldname": "rate_or_discount",
"fieldtype": "Select",
"label": "Rate or Discount",
- "options": "\nRate\nDiscount Percentage\nDiscount Amount",
- "reqd": 1
+ "options": "\nRate\nDiscount Percentage\nDiscount Amount"
},
{
"default": "Grand Total",
@@ -439,19 +438,20 @@
},
{
"default": "0",
- "depends_on": "eval:!doc.mixed_conditions",
+ "depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
"fieldname": "same_item",
"fieldtype": "Check",
"label": "Same Item"
},
{
- "depends_on": "eval:!doc.same_item || doc.mixed_conditions",
+ "depends_on": "eval:(!doc.same_item || doc.apply_on == 'Transaction') || doc.mixed_conditions",
"fieldname": "free_item",
"fieldtype": "Link",
"label": "Free Item",
"options": "Item"
},
{
+ "default": "0",
"fieldname": "free_qty",
"fieldtype": "Float",
"label": "Qty"
@@ -554,7 +554,7 @@
],
"icon": "fa fa-gift",
"idx": 1,
- "modified": "2019-10-15 12:39:40.399792",
+ "modified": "2019-12-18 17:29:22.957077",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index e871d98..3c14819 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -47,6 +47,9 @@
if tocheck and not self.get(tocheck):
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
+ if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
+ throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
+
def validate_applicable_for_selling_or_buying(self):
if not self.selling and not self.buying:
throw(_("Atleast one of the Selling or Buying must be selected"))
@@ -182,7 +185,7 @@
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
- get_applied_pricing_rules, get_pricing_rule_items)
+ get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
if isinstance(doc, string_types):
doc = json.loads(doc)
@@ -241,9 +244,11 @@
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
return item_details
- if (not pricing_rule.validate_applied_rule and
- pricing_rule.price_or_product_discount == "Price"):
- apply_price_discount_pricing_rule(pricing_rule, item_details, args)
+ if not pricing_rule.validate_applied_rule:
+ if pricing_rule.price_or_product_discount == "Price":
+ apply_price_discount_rule(pricing_rule, item_details, args)
+ else:
+ get_product_discount_rule(pricing_rule, item_details, doc)
item_details.has_pricing_rule = 1
@@ -293,7 +298,7 @@
'child_docname': args.get('child_docname')
})
-def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
+def apply_price_discount_rule(pricing_rule, item_details, args):
item_details.pricing_rule_for = pricing_rule.rate_or_discount
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 7af6748..87f6822 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -7,7 +7,7 @@
import frappe, copy, json
from frappe import throw, _
from six import string_types
-from frappe.utils import flt, cint, get_datetime
+from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
@@ -408,7 +408,8 @@
conditions = get_other_conditions(conditions, values, doc)
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
- where {conditions} """.format(conditions = conditions), values, as_dict=1)
+ where {conditions} and `tabPricing Rule`.disable = 0
+ """.format(conditions = conditions), values, as_dict=1)
if pricing_rules:
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
@@ -420,39 +421,65 @@
doc.set('apply_discount_on', d.apply_discount_on)
for field in ['additional_discount_percentage', 'discount_amount']:
- if not d.get(field): continue
-
pr_field = ('discount_percentage'
if field == 'additional_discount_percentage' else field)
+ if not d.get(pr_field): continue
+
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
frappe.msgprint(_("User has not applied rule on the invoice {0}")
.format(doc.name))
else:
doc.set(field, d.get(pr_field))
+
+ doc.calculate_taxes_and_totals()
elif d.price_or_product_discount == 'Product':
- apply_pricing_rule_for_free_items(doc, d)
+ item_details = frappe._dict({'parenttype': doc.doctype})
+ get_product_discount_rule(d, item_details, doc)
+ apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
+ doc.set_missing_values()
def get_applied_pricing_rules(item_row):
return (item_row.get("pricing_rules").split(',')
if item_row.get("pricing_rules") else [])
-def apply_pricing_rule_for_free_items(doc, pricing_rule):
- if pricing_rule.get('free_item'):
+def get_product_discount_rule(pricing_rule, item_details, doc=None):
+ free_item = (pricing_rule.free_item
+ if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code)
+
+ if not free_item:
+ frappe.throw(_("Free item not set in the pricing rule {0}")
+ .format(get_link_to_form("Pricing Rule", pricing_rule.name)))
+
+ item_details.free_item_data = {
+ 'item_code': free_item,
+ 'qty': pricing_rule.free_qty or 1,
+ 'rate': pricing_rule.free_item_rate or 0,
+ 'price_list_rate': pricing_rule.free_item_rate or 0,
+ 'is_free_item': 1
+ }
+
+ item_data = frappe.get_cached_value('Item', free_item, ['item_name',
+ 'description', 'stock_uom'], as_dict=1)
+
+ item_details.free_item_data.update(item_data)
+ item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
+ item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
+ item_details.free_item_data['uom']).get("conversion_factor", 1)
+
+ if item_details.get("parenttype") == 'Purchase Order':
+ item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
+
+ if item_details.get("parenttype") == 'Sales Order':
+ item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
+
+def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
+ if pricing_rule_args.get('item_code'):
items = [d.item_code for d in doc.items
- if d.item_code == (d.item_code
- if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
+ if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
if not items:
- doc.append('items', {
- 'item_code': pricing_rule.get('free_item'),
- 'qty': pricing_rule.get('free_qty'),
- 'uom': pricing_rule.get('free_item_uom'),
- 'rate': pricing_rule.get('free_item_rate') or 0,
- 'is_free_item': 1
- })
-
- doc.set_missing_values()
+ doc.append('items', pricing_rule_args)
def get_pricing_rule_items(pr_doc):
apply_on_data = []
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index d7e64cf..643de7d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -382,21 +382,11 @@
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
// filter on Account
- if (doc.supplier) {
- return {
- filters: {
- 'account_type': 'Payable',
- 'is_group': 0,
- 'company': doc.company
- }
- }
- } else {
- return {
- filters: {
- 'report_type': 'Balance Sheet',
- 'is_group': 0,
- 'company': doc.company
- }
+ return {
+ filters: {
+ 'account_type': 'Payable',
+ 'is_group': 0,
+ 'company': doc.company
}
}
}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 7f4ae3c..db6ac55 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -556,22 +556,11 @@
}
cur_frm.set_query("debit_to", function(doc) {
- // filter on Account
- if (doc.customer) {
- return {
- filters: {
- 'account_type': 'Receivable',
- 'is_group': 0,
- 'company': doc.company
- }
- }
- } else {
- return {
- filters: {
- 'report_type': 'Balance Sheet',
- 'is_group': 0,
- 'company': doc.company
- }
+ return {
+ filters: {
+ 'account_type': 'Receivable',
+ 'is_group': 0,
+ 'company': doc.company
}
}
});
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json
index f17bf04..59a3053 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.json
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json
@@ -188,7 +188,7 @@
}
],
"is_submittable": 1,
- "modified": "2019-11-07 13:31:17.999744",
+ "modified": "2019-12-20 14:48:01.990600",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Share Transfer",
@@ -196,6 +196,7 @@
"permissions": [
{
"amend": 1,
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -221,6 +222,7 @@
"write": 1
},
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -230,6 +232,7 @@
"report": 1,
"role": "Accounts Manager",
"share": 1,
+ "submit": 1,
"write": 1
}
],
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index feb598a..bb1b7e3 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -90,8 +90,12 @@
else:
merged_gl_map.append(entry)
+ company = gl_map[0].company if gl_map else erpnext.get_default_company()
+ company_currency = erpnext.get_company_currency(company)
+ precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
+
# filter zero debit and credit entries
- merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
+ merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
merged_gl_map = list(merged_gl_map)
return merged_gl_map
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 2c53f6e..f82146a 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -171,7 +171,7 @@
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
row.invoice_grand_total = row.invoiced
- if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
+ if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
# non-zero oustanding, we must consider this row
if self.is_invoice(row) and self.filters.based_on_payment_terms:
@@ -285,7 +285,7 @@
def set_party_details(self, row):
# customer / supplier name
- party_details = self.get_party_details(row.party)
+ party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get(scrub(self.filters.party_type)):
row.currency = row.account_currency
diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html
index 4081723..50947ec 100644
--- a/erpnext/accounts/report/financial_statements.html
+++ b/erpnext/accounts/report/financial_statements.html
@@ -1,5 +1,6 @@
{%
var report_columns = report.get_columns_for_print();
+ report_columns = report_columns.filter(col => !col.hidden);
if (report_columns.length > 8) {
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
@@ -15,34 +16,35 @@
height: 37px;
}
</style>
-{% 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 %}
-</div>
-{% } %}
+
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h3 class="text-center">{%= filters.company %}</h3>
+
{% if 'cost_center' in filters %}
<h3 class="text-center">{%= filters.cost_center %}</h3>
{% endif %}
+
<h3 class="text-center">{%= filters.fiscal_year %}</h3>
-<h5 class="text-center">{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %} </h4>
+<h5 class="text-center">
+ {%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %}
+</h5>
{% if (filters.from_date) { %}
- <h4 class="text-center">{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}</h3>
+ <h5 class="text-center">
+ {%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}
+ </h5>
{% } %}
<hr>
<table class="table table-bordered">
<thead>
<tr>
- <th style="width: {%= 100 - (report_columns.length - 2) * 13 %}%"></th>
- {% for(var i=2, l=report_columns.length; i<l; i++) { %}
+ <th style="width: {%= 100 - (report_columns.length - 1) * 13 %}%"></th>
+ {% for (let i=1, l=report_columns.length; i<l; i++) { %}
<th class="text-right">{%= report_columns[i].label %}</th>
{% } %}
</tr>
</thead>
<tbody>
- {% for(var j=0, k=data.length-1; j<k; j++) { %}
+ {% for(let j=0, k=data.length-1; j<k; j++) { %}
{%
var row = data[j];
var row_class = data[j].parent_account ? "" : "financial-statements-important";
@@ -52,11 +54,11 @@
<td>
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
</td>
- {% for(var i=2, l=report_columns.length; i<l; i++) { %}
+ {% for(let i=1, l=report_columns.length; i<l; i++) { %}
<td class="text-right">
- {% var fieldname = report_columns[i].fieldname; %}
+ {% const fieldname = report_columns[i].fieldname; %}
{% if (!is_null(row[fieldname])) { %}
- {%= format_currency(row[fieldname], filters.presentation_currency) %}
+ {%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
{% } %}
</td>
{% } %}
@@ -64,4 +66,6 @@
{% } %}
</tbody>
</table>
-<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
+<p class="text-right text-muted">
+ Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
+</p>
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 3c8de60..40d5682 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -264,8 +264,8 @@
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
total_row = {
- "account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
- "account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
+ "account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
+ "account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
"currency": company_currency
}
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index 0e2821a..afdd31d 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -38,32 +38,46 @@
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
- row = [
- inv.name, inv.posting_date, inv.customer, inv.customer_name
- ]
+ row = {
+ 'invoice': inv.name,
+ 'posting_date': inv.posting_date,
+ 'customer': inv.customer,
+ 'customer_name': inv.customer_name
+ }
if additional_query_columns:
for col in additional_query_columns:
- row.append(inv.get(col))
+ row.update({
+ col: inv.get(col)
+ })
- row +=[
- inv.get("customer_group"),
- inv.get("territory"),
- inv.get("tax_id"),
- inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])),
- inv.project, inv.owner, inv.remarks,
- ", ".join(sales_order), ", ".join(delivery_note),", ".join(cost_center),
- ", ".join(warehouse), company_currency
- ]
+ row.update({
+ 'customer_group': inv.get("customer_group"),
+ 'territory': inv.get("territory"),
+ 'tax_id': inv.get("tax_id"),
+ 'receivable_account': inv.debit_to,
+ 'mode_of_payment': ", ".join(mode_of_payments.get(inv.name, [])),
+ 'project': inv.project,
+ 'owner': inv.owner,
+ 'remarks': inv.remarks,
+ 'sales_order': ", ".join(sales_order),
+ 'delivery_note': ", ".join(delivery_note),
+ 'cost_center': ", ".join(cost_center),
+ 'warehouse': ", ".join(warehouse),
+ 'currency': company_currency
+ })
+
# map income values
base_net_total = 0
for income_acc in income_accounts:
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
base_net_total += income_amount
- row.append(income_amount)
+ row.update({
+ frappe.scrub(income_acc): income_amount
+ })
# net total
- row.append(base_net_total or inv.base_net_total)
+ row.update({'net_total': base_net_total or inv.base_net_total})
# tax account
total_tax = 0
@@ -72,10 +86,18 @@
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
total_tax += tax_amount
- row.append(tax_amount)
+ row.update({
+ frappe.scrub(tax_acc): tax_amount
+ })
# total tax, grand total, outstanding amount & rounded total
- row += [total_tax, inv.base_grand_total, inv.base_rounded_total, inv.outstanding_amount]
+
+ row.update({
+ 'tax_total': total_tax,
+ 'grand_total': inv.base_grand_total,
+ 'rounded_total': inv.base_rounded_total,
+ 'outstanding_amount': inv.outstanding_amount
+ })
data.append(row)
@@ -84,19 +106,118 @@
def get_columns(invoice_list, additional_table_columns):
"""return columns based on filters"""
columns = [
- _("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
- _("Customer") + ":Link/Customer:120", _("Customer Name") + "::120"
+ {
+ 'label': _("Invoice"),
+ 'fieldname': 'invoice',
+ 'fieldtype': 'Link',
+ 'options': 'Sales Invoice',
+ 'width': 120
+ },
+ {
+ 'label': _("Posting Date"),
+ 'fieldname': 'posting_date',
+ 'fieldtype': 'Date',
+ 'width': 80
+ },
+ {
+ 'label': _("Customer"),
+ 'fieldname': 'customer',
+ 'fieldtype': 'Link',
+ 'options': 'Customer',
+ 'width': 120
+ },
+ {
+ 'label': _("Customer Name"),
+ 'fieldname': 'customer_name',
+ 'fieldtype': 'Data',
+ 'width': 120
+ },
]
if additional_table_columns:
columns += additional_table_columns
columns +=[
- _("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
- _("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
- _("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150",
- _("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
- _("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100",
+ {
+ 'label': _("Custmer Group"),
+ 'fieldname': 'customer_group',
+ 'fieldtype': 'Link',
+ 'options': 'Customer Group',
+ 'width': 120
+ },
+ {
+ 'label': _("Territory"),
+ 'fieldname': 'territory',
+ 'fieldtype': 'Link',
+ 'options': 'Territory',
+ 'width': 80
+ },
+ {
+ 'label': _("Tax Id"),
+ 'fieldname': 'tax_id',
+ 'fieldtype': 'Data',
+ 'width': 120
+ },
+ {
+ 'label': _("Receivable Account"),
+ 'fieldname': 'receivable_account',
+ 'fieldtype': 'Link',
+ 'options': 'Account',
+ 'width': 80
+ },
+ {
+ 'label': _("Mode Of Payment"),
+ 'fieldname': 'mode_of_payment',
+ 'fieldtype': 'Data',
+ 'width': 120
+ },
+ {
+ 'label': _("Project"),
+ 'fieldname': 'project',
+ 'fieldtype': 'Link',
+ 'options': 'project',
+ 'width': 80
+ },
+ {
+ 'label': _("Owner"),
+ 'fieldname': 'owner',
+ 'fieldtype': 'Data',
+ 'width': 150
+ },
+ {
+ 'label': _("Remarks"),
+ 'fieldname': 'remarks',
+ 'fieldtype': 'Data',
+ 'width': 150
+ },
+ {
+ 'label': _("Sales Order"),
+ 'fieldname': 'sales_order',
+ 'fieldtype': 'Link',
+ 'options': 'Sales Order',
+ 'width': 100
+ },
+ {
+ 'label': _("Delivery Note"),
+ 'fieldname': 'delivery_note',
+ 'fieldtype': 'Link',
+ 'options': 'Delivery Note',
+ 'width': 100
+ },
+ {
+ 'label': _("Cost Center"),
+ 'fieldname': 'cost_center',
+ 'fieldtype': 'Link',
+ 'options': 'Cost Center',
+ 'width': 100
+ },
+ {
+ 'label': _("Warehouse"),
+ 'fieldname': 'warehouse',
+ 'fieldtype': 'Link',
+ 'options': 'Warehouse',
+ 'width': 100
+ },
{
"fieldname": "currency",
"label": _("Currency"),
@@ -105,7 +226,10 @@
}
]
- income_accounts = tax_accounts = income_columns = tax_columns = []
+ income_accounts = []
+ tax_accounts = []
+ income_columns = []
+ tax_columns = []
if invoice_list:
income_accounts = frappe.db.sql_list("""select distinct income_account
@@ -119,14 +243,65 @@
and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
- income_columns = [(account + ":Currency/currency:120") for account in income_accounts]
+ for account in income_accounts:
+ income_columns.append({
+ "label": account,
+ "fieldname": frappe.scrub(account),
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ })
+
for account in tax_accounts:
if account not in income_accounts:
- tax_columns.append(account + ":Currency/currency:120")
+ tax_columns.append({
+ "label": account,
+ "fieldname": frappe.scrub(account),
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ })
- columns = columns + income_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
- [_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
- _("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
+ net_total_column = [{
+ "label": _("Net Total"),
+ "fieldname": "net_total",
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ }]
+
+ total_columns = [
+ {
+ "label": _("Tax Total"),
+ "fieldname": "tax_total",
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ },
+ {
+ "label": _("Grand Total"),
+ "fieldname": "grand_total",
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ },
+ {
+ "label": _("Rounded Total"),
+ "fieldname": "rounded_total",
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ },
+ {
+ "label": _("Outstanding Amount"),
+ "fieldname": "outstanding_amount",
+ "fieldtype": "Currency",
+ "options": 'currency',
+ "width": 120
+ }
+ ]
+
+ columns = columns + income_columns + net_total_column + tax_columns + total_columns
return columns, income_accounts, tax_accounts
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 4e1822b..3a08baa 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -110,7 +110,7 @@
ORDER BY
asm.transaction_date asc
""", (d.asset, self.company, 'Receipt'), as_dict=1)
- if auto_gen_movement_entry[0].get('name') == self.name:
+ if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
auto generated for Asset {1}').format(self.name, d.asset))
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 5dce349..8cd44c7 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@@ -47,6 +48,7 @@
"ignore_pricing_rule",
"sec_warehouse",
"set_warehouse",
+ "set_reserve_warehouse",
"col_break_warehouse",
"is_subcontracted",
"supplier_warehouse",
@@ -1039,12 +1041,20 @@
"fieldtype": "Link",
"label": "Tax Category",
"options": "Tax Category"
+ },
+ {
+ "depends_on": "supplied_items",
+ "fieldname": "set_reserve_warehouse",
+ "fieldtype": "Link",
+ "label": "Set Reserve Warehouse",
+ "options": "Warehouse"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
- "modified": "2019-07-11 18:25:49.509343",
+ "links": [],
+ "modified": "2019-12-18 13:13:22.852412",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
index d3adcb7..ce3d8cf 100644
--- a/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
+++ b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
@@ -12,9 +12,10 @@
}
],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/supplier-onboard.png",
+ "image_src": "",
+ "is_completed": 0,
"max_count": 3,
- "modified": "2019-12-03 22:53:50.552445",
+ "modified": "2019-12-09 17:54:18.452038",
"modified_by": "Administrator",
"name": "Add A Few Suppliers",
"owner": "Administrator",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 75564af..6150516 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -319,8 +319,8 @@
if item.get('discount_amount'):
item.rate = item.price_list_rate - item.discount_amount
- elif pricing_rule_args.get('free_item'):
- apply_pricing_rule_for_free_items(self, pricing_rule_args)
+ elif pricing_rule_args.get('free_item_data'):
+ apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
elif pricing_rule_args.get("validate_applied_rule"):
for pricing_rule in get_applied_pricing_rules(item):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3ec7aff..75b896b 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -265,16 +265,17 @@
fg_yet_to_be_received = qty_to_be_received_map.get(item_key)
- raw_material_data = backflushed_raw_materials_map.get(item_key, {})
-
- consumed_qty = raw_material_data.get('qty', 0)
- consumed_serial_nos = raw_material_data.get('serial_nos', '')
- consumed_batch_nos = raw_material_data.get('batch_nos', '')
-
transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
for raw_material in transferred_raw_materials + non_stock_items:
+ rm_item_key = '{}{}'.format(raw_material.rm_item_code, item.purchase_order)
+ raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
+
+ consumed_qty = raw_material_data.get('qty', 0)
+ consumed_serial_nos = raw_material_data.get('serial_nos', '')
+ consumed_batch_nos = raw_material_data.get('batch_nos', '')
+
transferred_qty = raw_material.qty
rm_qty_to_be_consumed = transferred_qty - consumed_qty
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
index b6c4c47..f502930 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -11,7 +11,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import get_url
+from frappe.utils import get_url, getdate
from frappe.utils.verified_command import verify_request, get_signed_params
@@ -117,7 +117,7 @@
if self._assign:
return
available_agents = _get_agents_sorted_by_asc_workload(
- self.scheduled_time.date())
+ getdate(self.scheduled_time))
for agent in available_agents:
if(_check_agent_availability(agent, self.scheduled_time)):
agent = agent[0]
@@ -189,7 +189,7 @@
assigned_to = frappe.parse_json(appointment._assign)
if not assigned_to:
continue
- if (assigned_to[0] in agent_list) and appointment.scheduled_time.date() == date:
+ if (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date:
appointment_counter[assigned_to[0]] += 1
sorted_agent_list = appointment_counter.most_common()
sorted_agent_list.reverse()
diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js
index b6e741c..1936dcb 100644
--- a/erpnext/education/doctype/student/student.js
+++ b/erpnext/education/doctype/student/student.js
@@ -31,7 +31,7 @@
frappe.ui.form.on('Student Guardian', {
guardians_add: function(frm){
frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){
- var guardian_list = [];
+ let guardian_list = [];
if(!doc.__islocal) guardian_list.push(doc.guardian);
$.each(doc.guardians, function(idx, val){
if (val.guardian) guardian_list.push(val.guardian);
@@ -40,3 +40,18 @@
};
}
});
+
+
+frappe.ui.form.on('Student Sibling', {
+ siblings_add: function(frm){
+ frm.fields_dict['siblings'].grid.get_field('student').get_query = function(doc){
+ let sibling_list = [frm.doc.name];
+ $.each(doc.siblings, function(idx, val){
+ if (val.student && val.studying_in_same_institute == 'YES') {
+ sibling_list.push(val.student);
+ }
+ });
+ return { filters: [['Student', 'name', 'not in', sibling_list]] };
+ };
+ }
+});
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 2a5e6d8..c99ae7d 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -180,6 +180,7 @@
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
{"title": _("Certification"), "route": "/certification", "reference_doctype": "Certification Application", "role": "Non Profit Portal User"},
{"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"},
+ {"title": _("Appointment Booking"), "route": "/book_appointment"},
]
default_roles = [
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 9291820..a45b41d 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -232,7 +232,6 @@
"reqd": 1
},
{
- "description": "You can enter any date manually",
"fieldname": "date_of_birth",
"fieldtype": "Date",
"label": "Date of Birth",
@@ -831,4 +830,4 @@
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
index 996dcdf..c128567 100644
--- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
+++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js
@@ -7,6 +7,14 @@
frm.add_fetch("employee_onboarding_template", "department", "department");
frm.add_fetch("employee_onboarding_template", "designation", "designation");
frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade");
+
+ frm.set_query('job_offer', function () {
+ return {
+ filters: {
+ 'job_applicant': frm.doc.job_applicant
+ }
+ };
+ });
},
refresh: function(frm) {
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 9aeb7e8..e01e7a4 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -43,9 +43,9 @@
}[cstr(self.docstatus or 0)]
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
- precision = self.precision("total_sanctioned_amount")
+ precision = self.precision("grand_total")
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
- and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \
+ and flt(self.grand_total, precision) == flt(paid_amount, precision))) \
and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid"
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 915cea1..5222712 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -54,9 +54,11 @@
self.create_leave_ledger_entry()
self.reload()
+ def before_cancel(self):
+ self.status = "Cancelled"
+
def on_cancel(self):
self.create_leave_ledger_entry(submit=False)
- self.status = "Cancelled"
# notify leave applier about cancellation
self.notify_employee()
self.cancel_attendance()
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
index 2de01e6..dfd38eb 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
@@ -163,7 +163,7 @@
"""
cond = self.get_filter_condition()
return frappe.db.sql(""" select eld.loan_account, eld.loan,
- eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
+ eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment,t1.employee
from
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
where
@@ -246,6 +246,7 @@
accounts.append({
"account": acc,
"debit_in_account_currency": flt(amount, precision),
+ "party_type": '',
"cost_center": self.cost_center,
"project": self.project
})
@@ -257,6 +258,7 @@
"account": acc,
"credit_in_account_currency": flt(amount, precision),
"cost_center": self.cost_center,
+ "party_type": '',
"project": self.project
})
@@ -264,7 +266,9 @@
for data in loan_details:
accounts.append({
"account": data.loan_account,
- "credit_in_account_currency": data.principal_amount
+ "credit_in_account_currency": data.principal_amount,
+ "party_type": "Employee",
+ "party": data.employee
})
if data.interest_amount and not data.interest_income_account:
@@ -275,14 +279,17 @@
"account": data.interest_income_account,
"credit_in_account_currency": data.interest_amount,
"cost_center": self.cost_center,
- "project": self.project
+ "project": self.project,
+ "party_type": "Employee",
+ "party": data.employee
})
payable_amount -= flt(data.total_payment, precision)
# Payable amount
accounts.append({
"account": default_payroll_payable_account,
- "credit_in_account_currency": flt(payable_amount, precision)
+ "credit_in_account_currency": flt(payable_amount, precision),
+ "party_type": '',
})
journal_entry.set("accounts", accounts)
@@ -546,7 +553,6 @@
count += 1
if publish_progress:
frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
-
if submitted_ss:
payroll_entry.make_accrual_jv_entry()
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js
index dd34ef2..9f42c91 100755
--- a/erpnext/hr/doctype/salary_structure/salary_structure.js
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.js
@@ -46,7 +46,7 @@
frm.trigger("toggle_fields");
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
-
+
if(frm.doc.docstatus === 1) {
frm.add_custom_button(__("Preview Salary Slip"), function() {
frm.trigger('preview_salary_slip');
@@ -119,47 +119,52 @@
},
callback: function(r) {
var employees = r.message;
- var d = new frappe.ui.Dialog({
- title: __("Preview Salary Slip"),
- fields: [
- {
- "label":__("Employee"),
- "fieldname":"employee",
- "fieldtype":"Select",
- "reqd": true,
- options: employees
- }, {
- fieldname:"fetch",
- "label":__("Show Salary Slip"),
- "fieldtype":"Button"
- }
- ]
- });
- d.get_input("fetch").on("click", function() {
- var values = d.get_values();
- if(!values) return;
- var print_format;
- frm.doc.salary_slip_based_on_timesheet ?
- print_format="Salary Slip based on Timesheet" :
- print_format="Salary Slip Standard";
-
- frappe.call({
- method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
- args: {
- source_name: frm.doc.name,
- employee: values.employee,
- as_print: 1,
- print_format: print_format,
- for_preview: 1
- },
- callback: function(r) {
- var new_window = window.open();
- new_window.document.write(r.message);
- // frappe.msgprint(r.message);
- }
+ if(!employees) return;
+ if (employees.length == 1){
+ frm.events.open_salary_slip(frm, employees[0]);
+ } else {
+ var d = new frappe.ui.Dialog({
+ title: __("Preview Salary Slip"),
+ fields: [
+ {
+ "label":__("Employee"),
+ "fieldname":"employee",
+ "fieldtype":"Select",
+ "reqd": true,
+ options: employees
+ }, {
+ fieldname:"fetch",
+ "label":__("Show Salary Slip"),
+ "fieldtype":"Button"
+ }
+ ]
});
- });
- d.show();
+ d.get_input("fetch").on("click", function() {
+ var values = d.get_values();
+ if(!values) return;
+ frm.events.open_salary_slip(frm, values.employee)
+
+ });
+ d.show();
+ }
+ }
+ });
+ },
+
+ open_salary_slip: function(frm, employee){
+ var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard";
+ frappe.call({
+ method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
+ args: {
+ source_name: frm.doc.name,
+ employee: employee,
+ as_print: 1,
+ print_format: print_format,
+ for_preview: 1
+ },
+ callback: function(r) {
+ var new_window = window.open();
+ new_window.document.write(r.message);
}
});
},
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html
index b1aeb2c..374038a 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.html
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.html
@@ -1,9 +1,44 @@
-<h3>{{_("Training Event")}}</h3>
+<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr height="10"></tr>
+ <tr>
+ <td width="15"></td>
+ <td>
+ <div class="text-medium text-muted">
+ <span>{{_("Training Event:")}} {{ doc.event_name }}</span>
+ </div>
+ </td>
+ <td width="15"></td>
+ </tr>
+ <tr height="10"></tr>
+</table>
-<p>{{ doc.introduction }}</p>
-
-<h4>{{_("Details")}}</h4>
-{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
-<br>{{_("Event Location")}}: {{ doc.location }}
-<br>{{_("Start Time")}}: {{ doc.start_time }}
-<br>{{_("End Time")}}: {{ doc.end_time }}
+<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr height="10"></tr>
+ <tr>
+ <td width="15"></td>
+ <td>
+ <div>
+ {{ doc.introduction }}
+ <ul class="list-unstyled" style="line-height: 1.7">
+ <li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
+ {% set start = frappe.utils.get_datetime(doc.start_time) %}
+ {% set end = frappe.utils.get_datetime(doc.end_time) %}
+ {% if start.date() == end.date() %}
+ <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
+ <li>
+ {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
+ </li>
+ {% else %}
+ <li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+ </li>
+ <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+ </li>
+ {% endif %}
+ <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+ </ul>
+ </div>
+ </td>
+ <td width="15"></td>
+ </tr>
+ <tr height="10"></tr>
+</table>
\ No newline at end of file
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json
index c07e1a6..966b887 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.json
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.json
@@ -1,5 +1,7 @@
{
"attach_print": 0,
+ "channel": "Email",
+ "condition": "",
"creation": "2017-08-11 03:13:40.519614",
"days_in_advance": 0,
"docstatus": 0,
@@ -9,8 +11,8 @@
"event": "Submit",
"idx": 0,
"is_standard": 1,
- "message": "<h3>{{_(\"Training Event\")}}</h3>\n\n<p>{{ doc.introduction }}</p>\n\n<h4>{{_(\"Details\")}}</h4>\n{{_(\"Event Name\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n<br>{{_(\"Event Location\")}}: {{ doc.location }}\n<br>{{_(\"Start Time\")}}: {{ doc.start_time }}\n<br>{{_(\"End Time\")}}: {{ doc.end_time }}\n",
- "modified": "2017-08-13 22:49:42.338881",
+ "message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n <tr height=\"10\"></tr>\n <tr>\n <td width=\"15\"></td>\n <td>\n <div class=\"text-medium text-muted\">\n <span>{{_(\"Training Event:\")}} {{ doc.event_name }}</span>\n </div>\n </td>\n <td width=\"15\"></td>\n </tr>\n <tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n <tr height=\"10\"></tr>\n <tr>\n <td width=\"15\"></td>\n <td>\n <div>\n <ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n <li>{{ doc.introduction }}</li>\n <li>{{_(\"Event Location\")}}: <b>{{ doc.location }}</b></li>\n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n <li>{{_(\"Date\")}}: <b>{{ start.strftime(\"%A, %d %b %Y\") }}</b></li>\n <li>\n {{_(\"Timing\")}}: <b>{{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}</b>\n </li>\n {% else %}\n <li>{{_(\"Start Time\")}}: <b>{{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n </li>\n <li>{{_(\"End Time\")}}: <b>{{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n </li>\n {% endif %}\n </ul>\n {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n </div>\n </td>\n <td width=\"15\"></td>\n </tr>\n <tr height=\"10\"></tr>\n</table>",
+ "modified": "2019-11-29 15:38:31.805409",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Scheduled",
diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md
index bcadf7d..374038a 100644
--- a/erpnext/hr/notification/training_scheduled/training_scheduled.md
+++ b/erpnext/hr/notification/training_scheduled/training_scheduled.md
@@ -1,9 +1,44 @@
-<h3>{{_("Training Event")}}</h3>
-<p>{{ message }}</p>
+<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr height="10"></tr>
+ <tr>
+ <td width="15"></td>
+ <td>
+ <div class="text-medium text-muted">
+ <span>{{_("Training Event:")}} {{ doc.event_name }}</span>
+ </div>
+ </td>
+ <td width="15"></td>
+ </tr>
+ <tr height="10"></tr>
+</table>
-<h4>{{_("Details")}}</h4>
-{{_("Event Name")}}: <a href="{{ event_link }}">{{ name }}</a>
-<br>{{_("Event Location")}}: {{ location }}
-<br>{{_("Start Time")}}: {{ start_time }}
-<br>{{_("End Time")}}: {{ end_time }}
-<br>{{_("Attendance")}}: {{ attendance }}
+<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr height="10"></tr>
+ <tr>
+ <td width="15"></td>
+ <td>
+ <div>
+ {{ doc.introduction }}
+ <ul class="list-unstyled" style="line-height: 1.7">
+ <li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
+ {% set start = frappe.utils.get_datetime(doc.start_time) %}
+ {% set end = frappe.utils.get_datetime(doc.end_time) %}
+ {% if start.date() == end.date() %}
+ <li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
+ <li>
+ {{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
+ </li>
+ {% else %}
+ <li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+ </li>
+ <li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
+ </li>
+ {% endif %}
+ <li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+ </ul>
+ </div>
+ </td>
+ <td width="15"></td>
+ </tr>
+ <tr height="10"></tr>
+</table>
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index e3ece56..f8146bb 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -65,6 +65,7 @@
context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
def on_update(self):
+ frappe.cache().hdel('bom_children', self.name)
self.check_recursion()
self.update_stock_qty()
self.update_exploded_items()
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 8ca8917..176ca2e 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -605,6 +605,8 @@
description: __('Max: {0}', [max]),
default: max
}, data => {
+ max += (max * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;
+
if (data.qty > max) {
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
reject();
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 227ef78..c4238ac 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -38,7 +38,7 @@
ms = frappe.get_doc("Manufacturing Settings")
self.set_onload("material_consumption", ms.material_consumption)
self.set_onload("backflush_raw_materials_based_on", ms.backflush_raw_materials_based_on)
-
+ self.set_onload("overproduction_percentage", ms.overproduction_percentage_for_work_order)
def validate(self):
self.validate_production_item()
@@ -657,8 +657,9 @@
wo_doc = frappe.new_doc("Work Order")
wo_doc.production_item = item
wo_doc.update(item_details)
- if qty > 0:
- wo_doc.qty = qty
+
+ if flt(qty) > 0:
+ wo_doc.qty = flt(qty)
wo_doc.get_items_and_operations_from_bom()
return wo_doc
diff --git a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
index c565b7e..5bb6e3f 100644
--- a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
+++ b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py
@@ -27,6 +27,8 @@
tax_category = inter_state_category.name
for doctype in ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template'):
+ if not frappe.get_meta(doctype).has_field('is_inter_state'): continue
+
template = frappe.db.get_value(doctype, {'is_inter_state': 1, 'disabled': 0}, ['name'])
if template:
frappe.db.set_value(doctype, template, 'tax_category', tax_category)
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 3a373a4..0993e69 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -52,7 +52,6 @@
def get_products_for_website(field_filters=None, attribute_filters=None, search=None):
-
if attribute_filters:
item_codes = get_item_codes_by_attributes(attribute_filters)
items_by_attributes = get_items([['name', 'in', item_codes]])
@@ -302,6 +301,8 @@
if isinstance(filters, dict):
filters = [['Item', fieldname, '=', value] for fieldname, value in filters.items()]
+ enabled_items_filter = get_conditions({ 'disabled': 0 }, 'and')
+
show_in_website_condition = ''
if products_settings.hide_variants:
show_in_website_condition = get_conditions({'show_in_website': 1 }, 'and')
@@ -337,7 +338,8 @@
filter_condition = get_conditions(filters, 'and')
where_conditions = ' and '.join(
- [condition for condition in [show_in_website_condition, search_condition, filter_condition] if condition]
+ [condition for condition in [enabled_items_filter, show_in_website_condition, \
+ search_condition, filter_condition] if condition]
)
left_joins = []
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 7083d69..45f2681 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -47,11 +47,11 @@
if not self.project or frappe.flags.in_test:
return
- expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
+ expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
if expected_end_date:
- validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
- validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
+ validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
+ validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
def validate_status(self):
if self.status!=self.get_db_value("status") and self.status == "Completed":
@@ -278,4 +278,4 @@
frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
- frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
\ No newline at end of file
+ frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
diff --git a/erpnext/public/images/illustrations/customers-onboard.png b/erpnext/public/images/illustrations/customers-onboard.png
deleted file mode 100644
index 4a517bd..0000000
--- a/erpnext/public/images/illustrations/customers-onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/desk-onboard.png b/erpnext/public/images/illustrations/desk-onboard.png
deleted file mode 100644
index 74b632d..0000000
--- a/erpnext/public/images/illustrations/desk-onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/letterhead-onboard.png b/erpnext/public/images/illustrations/letterhead-onboard.png
deleted file mode 100644
index fdfd16a..0000000
--- a/erpnext/public/images/illustrations/letterhead-onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/products-onboard.png b/erpnext/public/images/illustrations/products-onboard.png
deleted file mode 100644
index 2dee203..0000000
--- a/erpnext/public/images/illustrations/products-onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/supplier-onboard.png b/erpnext/public/images/illustrations/supplier-onboard.png
deleted file mode 100644
index 30335f2..0000000
--- a/erpnext/public/images/illustrations/supplier-onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 46a58fb..6ca0958 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -500,6 +500,9 @@
() => {
var d = locals[cdt][cdn];
me.add_taxes_from_item_tax_template(d.item_tax_rate);
+ if (d.free_item_data) {
+ me.apply_product_discount(d.free_item_data);
+ }
},
() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
() => me.toggle_conversion_factor(item),
@@ -1305,6 +1308,10 @@
me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
}
+ if (d.free_item_data) {
+ me.apply_product_discount(d.free_item_data);
+ }
+
if (d.apply_rule_on_other_items) {
items_rule_dict[d.name] = d;
}
@@ -1334,6 +1341,20 @@
}
},
+ apply_product_discount: function(free_item_data) {
+ const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code
+ && d.is_free_item)) || [];
+
+ if (!items.length) {
+ let row_to_modify = frappe.model.add_child(this.frm.doc,
+ this.frm.doc.doctype + ' Item', 'items');
+
+ for (let key in free_item_data) {
+ row_to_modify[key] = free_item_data[key];
+ }
+ }
+ },
+
apply_price_list: function(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
@@ -1754,14 +1775,28 @@
}
},
+ set_reserve_warehouse: function() {
+ this.autofill_warehouse("reserve_warehouse");
+ },
+
set_warehouse: function() {
+ this.autofill_warehouse("warehouse");
+ },
+
+ autofill_warehouse : function (warehouse_field) {
+ // set warehouse in all child table rows
var me = this;
- if(this.frm.doc.set_warehouse) {
- $.each(this.frm.doc.items || [], function(i, item) {
- frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse);
+ let warehouse = (warehouse_field === "warehouse") ? me.frm.doc.set_warehouse : me.frm.doc.set_reserve_warehouse;
+ let child_table = (warehouse_field === "warehouse") ? me.frm.doc.items : me.frm.doc.supplied_items;
+ let doctype = (warehouse_field === "warehouse") ? (me.frm.doctype + " Item") : (me.frm.doctype + " Item Supplied");
+
+ if(warehouse) {
+ $.each(child_table || [], function(i, item) {
+ frappe.model.set_value(doctype, item.name, warehouse_field, warehouse);
});
}
},
+
coupon_code: function() {
var me = this;
frappe.run_serially([
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index d5a78d4..f363999 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -501,6 +501,7 @@
frm.doc[opts.child_docname].forEach(d => {
dialog.fields_dict.trans_items.df.data.push({
"docname": d.name,
+ "name": d.name,
"item_code": d.item_code,
"qty": d.qty,
"rate": d.rate,
diff --git a/erpnext/regional/report/gstr_2/gstr_2.py b/erpnext/regional/report/gstr_2/gstr_2.py
index a362269..f326fe0 100644
--- a/erpnext/regional/report/gstr_2/gstr_2.py
+++ b/erpnext/regional/report/gstr_2/gstr_2.py
@@ -44,12 +44,16 @@
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv)
for rate, items in items_based_on_rate.items():
- row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
- tax_amount = taxable_value * rate / 100
- if inv in self.igst_invoices:
- row += [tax_amount, 0, 0]
+ if inv not in self.igst_invoices:
+ rate = rate / 2
+ row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
+ tax_amount = taxable_value * rate / 100
+ row += [0, tax_amount, tax_amount]
else:
- row += [0, tax_amount / 2, tax_amount / 2]
+ row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
+ tax_amount = taxable_value * rate / 100
+ row += [tax_amount, 0, 0]
+
row += [
self.invoice_cess.get(inv),
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index cca8efe..aa1b92f 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -5,13 +5,13 @@
setup: function(frm) {
frm.make_methods = {
- 'Quotation': () => erpnext.utils.create_new_doc('Quotation', {
- 'quotation_to': frm.doc.doctype,
- 'party_name': frm.doc.name
+ 'Quotation': () => frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.customer.customer.make_quotation",
+ frm: cur_frm
}),
- 'Opportunity': () => erpnext.utils.create_new_doc('Opportunity', {
- 'opportunity_from': frm.doc.doctype,
- 'party_name': frm.doc.name
+ 'Opportunity': () => frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.customer.customer.make_opportunity",
+ frm: cur_frm
})
}
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 57308ce..136236c 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -12,6 +12,7 @@
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.model.rename_doc import update_linked_doctypes
+from frappe.model.mapper import get_mapped_doc
class Customer(TransactionBase):
def get_feed(self):
@@ -239,6 +240,66 @@
contact.insert()
@frappe.whitelist()
+def make_quotation(source_name, target_doc=None):
+
+ def set_missing_values(source, target):
+ _set_missing_values(source, target)
+
+ target_doc = get_mapped_doc("Customer", source_name,
+ {"Customer": {
+ "doctype": "Quotation",
+ "field_map": {
+ "name":"party_name"
+ }
+ }}, target_doc, set_missing_values)
+
+ target_doc.quotation_to = "Customer"
+ target_doc.run_method("set_missing_values")
+ target_doc.run_method("set_other_charges")
+ target_doc.run_method("calculate_taxes_and_totals")
+
+ price_list = frappe.get_value("Customer", source_name, 'default_price_list')
+ if price_list:
+ target_doc.selling_price_list = price_list
+
+ return target_doc
+
+@frappe.whitelist()
+def make_opportunity(source_name, target_doc=None):
+ def set_missing_values(source, target):
+ _set_missing_values(source, target)
+
+ target_doc = get_mapped_doc("Customer", source_name,
+ {"Customer": {
+ "doctype": "Opportunity",
+ "field_map": {
+ "name": "party_name",
+ "doctype": "opportunity_from",
+ }
+ }}, target_doc, set_missing_values)
+
+ return target_doc
+
+def _set_missing_values(source, target):
+ address = frappe.get_all('Dynamic Link', {
+ 'link_doctype': source.doctype,
+ 'link_name': source.name,
+ 'parenttype': 'Address',
+ }, ['parent'], limit=1)
+
+ contact = frappe.get_all('Dynamic Link', {
+ 'link_doctype': source.doctype,
+ 'link_name': source.name,
+ 'parenttype': 'Contact',
+ }, ['parent'], limit=1)
+
+ if address:
+ target.customer_address = address[0].parent
+
+ if contact:
+ target.contact_person = contact[0].parent
+
+@frappe.whitelist()
def get_loyalty_programs(doc):
''' returns applicable loyalty programs for a customer '''
from frappe.desk.treeview import get_children
diff --git a/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
index f39fea4..92d00bc 100644
--- a/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
+++ b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
@@ -12,9 +12,10 @@
}
],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/customers-onboard.png",
+ "image_src": "",
+ "is_completed": 0,
"max_count": 3,
- "modified": "2019-12-03 22:54:28.959549",
+ "modified": "2019-12-09 17:54:01.686006",
"modified_by": "Administrator",
"name": "Add A Few Customers",
"owner": "Administrator",
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 b213a29..33fbc22 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -286,14 +286,14 @@
if (in_list(['serial_no', 'batch_no'], field)) {
args[field] = value;
}
-
+
// add to cur_frm
const item = this.frm.add_child('items', args);
frappe.flags.hide_serial_batch_dialog = true;
frappe.run_serially([
() => {
- this.frm.script_manager.trigger('item_code', item.doctype, item.name)
+ return this.frm.script_manager.trigger('item_code', item.doctype, item.name)
.then(() => {
this.frm.script_manager.trigger('qty', item.doctype, item.name)
.then(() => {
diff --git a/erpnext/setup/doctype/company/company_list.js b/erpnext/setup/doctype/company/company_list.js
new file mode 100644
index 0000000..0172865
--- /dev/null
+++ b/erpnext/setup/doctype/company/company_list.js
@@ -0,0 +1,10 @@
+frappe.listview_settings['Company'] = {
+ onload: () => {
+ frappe.breadcrumbs.add({
+ type: 'Custom',
+ module: __('Accounts'),
+ label: __('Accounts'),
+ route: '#modules/Accounts'
+ });
+ }
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 637e655..1503adb 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -106,7 +106,10 @@
frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
def delete_communications(doctype, company_name, company_fieldname):
- frappe.db.sql("""
- DELETE FROM `tabCommunication` WHERE reference_doctype = %s AND
- EXISTS (SELECT name FROM `tab{0}` WHERE {1} = %s AND `tabCommunication`.reference_name = name)
- """.format(doctype, company_fieldname), (doctype, company_name))
+ reference_docs = frappe.get_all(doctype, filters={company_fieldname:company_name})
+ reference_doc_names = [r.name for r in reference_docs]
+
+ communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in", reference_doc_names]})
+ communication_names = [c.name for c in communications]
+
+ frappe.delete_doc("Communication", communication_names)
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index 8d9c23a..1664b66 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -88,6 +88,57 @@
self.delete_mode_of_payment(template)
frappe.delete_doc("Company", template)
+ def test_delete_communication(self):
+ from erpnext.setup.doctype.company.delete_company_transactions import delete_communications
+ company = create_child_company()
+ lead = create_test_lead_in_company(company)
+ communication = create_company_communication("Lead", lead)
+ delete_communications("Lead", "Test Company", "company")
+ self.assertFalse(frappe.db.exists("Communcation", communication))
+ self.assertFalse(frappe.db.exists({"doctype":"Comunication Link", "link_name": communication}))
+
def delete_mode_of_payment(self, company):
frappe.db.sql(""" delete from `tabMode of Payment Account`
where company =%s """, (company))
+
+def create_company_communication(doctype, docname):
+ comm = frappe.get_doc({
+ "doctype": "Communication",
+ "communication_type": "Communication",
+ "content": "Deduplication of Links",
+ "communication_medium": "Email",
+ "reference_doctype":doctype,
+ "reference_name":docname
+ })
+ comm.insert()
+
+def create_child_company():
+ child_company = frappe.db.exists("Company", "Test Company")
+ if not child_company:
+ child_company = frappe.get_doc({
+ "doctype":"Company",
+ "company_name":"Test Company",
+ "abbr":"test_company",
+ "default_currency":"INR"
+ })
+ child_company.insert()
+ else:
+ child_company = frappe.get_doc("Company", child_company)
+
+ return child_company.name
+
+def create_test_lead_in_company(company):
+ lead = frappe.db.exists("Lead", "Test Lead in new company")
+ if not lead:
+ lead = frappe.get_doc({
+ "doctype": "Lead",
+ "lead_name": "Test Lead in new company",
+ "scompany": company
+ })
+ lead.insert()
+ else:
+ lead = frappe.get_doc("Lead", lead)
+ lead.company = company
+ lead.save()
+ return lead.name
+
diff --git "a/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
index bf330d0..f00dc94 100644
--- "a/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
+++ "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
@@ -7,10 +7,10 @@
"domains": [],
"help_links": [],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
+ "image_src": "",
"is_completed": 0,
"max_count": 3,
- "modified": "2019-12-04 19:21:39.995776",
+ "modified": "2019-12-09 17:53:53.849953",
"modified_by": "Administrator",
"name": "Welcome back to ERPNext!",
"owner": "Administrator",
diff --git "a/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
index 4ea6985..37eb67b 100644
--- "a/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
+++ "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
@@ -7,13 +7,14 @@
"domains": [],
"help_links": [],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
+ "image_src": "",
+ "is_completed": 0,
"max_count": 0,
- "modified": "2019-12-03 22:49:12.871260",
+ "modified": "2019-12-22 21:26:28.414597",
"modified_by": "Administrator",
"name": "Welcome to ERPNext!",
"owner": "Administrator",
- "slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
+ "slide_desc": "<div class=\"text center\">Setting up an ERP can be overwhelming. But don't worry, we have got your back! This wizard will help you onboard to ERPNext in a short time!</div>",
"slide_fields": [],
"slide_module": "Setup",
"slide_order": 1,
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index a2aab3f..af8e132 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -135,8 +135,7 @@
"publish_in_hub",
"hub_category_to_publish",
"hub_warehouse",
- "synced_with_hub",
- "manufacturers"
+ "synced_with_hub"
],
"fields": [
{
@@ -1017,12 +1016,6 @@
"read_only": 1
},
{
- "fieldname": "manufacturers",
- "fieldtype": "Table",
- "label": "Manufacturers",
- "options": "Item Manufacturer"
- },
- {
"depends_on": "eval:!doc.__islocal",
"fieldname": "over_delivery_receipt_allowance",
"fieldtype": "Float",
@@ -1049,7 +1042,7 @@
"idx": 2,
"image_field": "image",
"max_attachments": 1,
- "modified": "2019-10-09 17:05:59.576119",
+ "modified": "2019-12-13 12:15:56.197246",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 189261c..151be11 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -125,7 +125,6 @@
self.validate_auto_reorder_enabled_in_stock_settings()
self.cant_change()
self.update_show_in_website()
- self.validate_manufacturer()
if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
@@ -145,13 +144,6 @@
if cint(frappe.db.get_single_value('Stock Settings', 'clean_description_html')):
self.description = clean_html(self.description)
- def validate_manufacturer(self):
- list_man = [(x.manufacturer, x.manufacturer_part_no) for x in self.get('manufacturers')]
- set_man = set(list_man)
-
- if len(list_man) != len(set_man):
- frappe.throw(_("Duplicate entry in Manufacturers table"))
-
def validate_customer_provided_part(self):
if self.is_customer_provided_item:
if self.is_purchase_item:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 00d27ef..1b9660e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -372,7 +372,7 @@
elif d.t_warehouse and not d.basic_rate:
d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
self.doctype, self.name, d.allow_zero_valuation_rate,
- currency=erpnext.get_company_currency(self.company))
+ currency=erpnext.get_company_currency(self.company), company=self.company)
def set_actual_qty(self):
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
diff --git a/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
index 27a3062..5ee3167 100644
--- a/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
+++ b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
@@ -7,9 +7,10 @@
"domains": [],
"help_links": [],
"idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/products-onboard.png",
+ "image_src": "",
+ "is_completed": 0,
"max_count": 3,
- "modified": "2019-12-03 22:54:07.558632",
+ "modified": "2019-12-09 17:54:09.602885",
"modified_by": "Administrator",
"name": "Add A Few Products You Buy Or Sell",
"owner": "Administrator",
diff --git a/erpnext/www/book_appointment/index.js b/erpnext/www/book_appointment/index.js
index c8dd501..262e31b 100644
--- a/erpnext/www/book_appointment/index.js
+++ b/erpnext/www/book_appointment/index.js
@@ -24,20 +24,15 @@
}
function setup_timezone_selector() {
- /**
- * window.timezones is a dictionary with the following structure
- * { IANA name: Pretty name}
- * For example : { Asia/Kolkata : "India Time - Asia/Kolkata"}
- */
let timezones_element = document.getElementById('appointment-timezone');
- let offset = new Date().getTimezoneOffset();
- Object.keys(window.timezones).forEach((timezone) => {
+ let local_timezone = moment.tz.guess()
+ window.timezones.forEach(timezone => {
let opt = document.createElement('option');
opt.value = timezone;
- if (timezone == moment.tz.guess()) {
+ if (timezone == local_timezone) {
opt.selected = true;
}
- opt.innerHTML = window.timezones[timezone]
+ opt.innerHTML = timezone;
timezones_element.appendChild(opt)
});
}
@@ -114,7 +109,7 @@
timeslot_div.classList.add('unavailable')
}
timeslot_div.innerHTML = get_slot_layout(start_time);
- timeslot_div.id = timeslot.time.substr(11, 20);
+ timeslot_div.id = timeslot.time.substring(11, 19);
timeslot_div.addEventListener('click', select_time);
return timeslot_div
}
diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py
index 5b60dd5..7bfac89 100644
--- a/erpnext/www/book_appointment/index.py
+++ b/erpnext/www/book_appointment/index.py
@@ -25,18 +25,8 @@
@frappe.whitelist(allow_guest=True)
def get_timezones():
- from babel.dates import get_timezone, get_timezone_name, Locale
- from frappe.utils.momentjs import get_all_timezones
-
- translated_dict = {}
- locale = Locale.parse(frappe.local.lang, sep="-")
-
- for tz in get_all_timezones():
- timezone_name = get_timezone_name(get_timezone(tz), locale=locale, width='short')
- if timezone_name:
- translated_dict[tz] = timezone_name + ' - ' + tz
-
- return translated_dict
+ import pytz
+ return pytz.all_timezones
@frappe.whitelist(allow_guest=True)
def get_appointment_slots(date, timezone):
@@ -90,7 +80,7 @@
@frappe.whitelist(allow_guest=True)
def create_appointment(date, time, tz, contact):
- format_string = '%Y-%m-%d %H:%M:%S%z'
+ format_string = '%Y-%m-%d %H:%M:%S'
scheduled_time = datetime.datetime.strptime(date + " " + time, format_string)
# Strip tzinfo from datetime objects since it's handled by the doctype
scheduled_time = scheduled_time.replace(tzinfo = None)