Merge branch 'develop' of github.com:frappe/erpnext into employee-leave-balance-summary

This commit is contained in:
Suraj Shetty 2019-09-07 22:02:02 +05:30
commit a14ee7f1fe
48 changed files with 962 additions and 3401 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '12.1.0'
__version__ = '12.1.1'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -12,11 +12,14 @@ from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist()
@cache_source
def get(chart_name=None, from_date = None, to_date = None):
chart = frappe.get_doc('Dashboard Chart', chart_name)
def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_date = None):
if chart_name:
chart = frappe.get_doc('Dashboard Chart', chart_name)
else:
chart = frappe._dict(frappe.parse_json(chart))
timespan = chart.timespan
timegrain = chart.time_interval
filters = json.loads(chart.filters_json)
filters = frappe.parse_json(chart.filters_json)
account = filters.get("account")
company = filters.get("company")

View File

@ -406,7 +406,11 @@
"is_group": 1,
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
"account_number": "9960"
},
},
"Debitoren": {
"is_group": 1,
"account_number": "10000"
},
"Forderungen aus Lieferungen und Leistungen": {
"account_number": "1200",
"account_type": "Receivable"
@ -1077,7 +1081,7 @@
}
}
},
"C - Verb.": {
"C - Verbindlichkeiten": {
"account_type": "Payable",
"1 - Anleihen": {
"is_group": 1,
@ -1193,7 +1197,15 @@
"is_group": 1,
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
"account_number": "9964"
},
},
"Kreditoren": {
"account_number": "70000",
"is_group": 1,
"Wareneingangs-­Verrechnungskonto" : {
"account_number": "70001",
"account_type": "Stock Received But Not Billed"
}
},
"Verb. aus Lieferungen und Leistungen": {
"account_number": "3300",
"account_type": "Payable"
@ -1682,90 +1694,6 @@
"account_type": "Income Account"
}
},
"Erl\u00f6sschm\u00e4lerungen (Gruppe)": {
"is_group": 1,
"Erl\u00f6sschm\u00e4lerungen": {
"account_number": "4700"
},
"Erl\u00f6sschm\u00e4lerungen aus steuerfreien Ums\u00e4tzen \u00a7 4 Nr. 1a UStG": {
"account_number": "4705"
},
"Erl\u00f6sschm\u00e4lerungen 7 % USt": {
"account_number": "4710"
},
"Erl\u00f6sschm\u00e4lerungen 19 % USt": {
"account_number": "4720"
},
"Erl\u00f6sschm\u00e4lerungen 16 % USt": {
"account_number": "4723"
},
"Erl\u00f6sschm\u00e4lerungen aus steuerfreien innergem. Lieferungen": {
"account_number": "4724"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 7 % USt": {
"account_number": "4725"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 19 % USt": {
"account_number": "4726"
},
"Erl\u00f6sschm\u00e4lerungen aus im anderen EU-Land steuerpfl. Lieferungen": {
"account_number": "4727"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
"account_number": "4729"
},
"Gew\u00e4hrte Skonti (Gruppe)": {
"is_group": 1,
"Gew. Skonti": {
"account_number": "4730"
},
"Gew. Skonti 7 % USt": {
"account_number": "4731"
},
"Gew. Skonti 19 % USt": {
"account_number": "4736"
},
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4738"
},
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
"account_number": "4741"
},
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4742"
},
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
"account_number": "4743"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
"account_number": "4745"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
"account_number": "4746"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
"account_number": "4748"
}
},
"Gew\u00e4hrte Boni 7 % USt": {
"account_number": "4750"
},
"Gew\u00e4hrte Boni 19 % USt": {
"account_number": "4760"
},
"Gew\u00e4hrte Boni": {
"account_number": "4769"
},
"Gew\u00e4hrte Rabatte": {
"account_number": "4770"
},
"Gew\u00e4hrte Rabatte 7 % USt": {
"account_number": "4780"
},
"Gew\u00e4hrte Rabatte 19 % USt": {
"account_number": "4790"
}
},
"Grundst\u00fccksertr\u00e4ge (Gruppe)": {
"is_group": 1,
"Grundst\u00fccksertr\u00e4ge": {
@ -2049,48 +1977,6 @@
"Erh. Skonti aus Erwerb Waren als letzter Abnehmer innerh. Dreiecksgesch. 19% Vorst. u. 19% Ust.": {
"account_number": "5793"
}
},
"Erhaltene Boni (Gruppe)": {
"is_group": 1,
"Erhaltene Boni 7 % Vorsteuer": {
"account_number": "5750"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
"account_number": "5753"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
"account_number": "5754"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
"account_number": "5755"
},
"Erhaltene Boni 19 % Vorsteuer": {
"account_number": "5760"
},
"Erhaltene Boni": {
"account_number": "5769"
}
},
"Erhaltene Rabatte (Gruppe)": {
"is_group": 1,
"Erhaltene Rabatte": {
"account_number": "5770"
},
"Erhaltene Rabatte 7 % Vorsteuer": {
"account_number": "5780"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
"account_number": "5783"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
"account_number": "5784"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
"account_number": "5785"
},
"Erhaltene Rabatte 19 % Vorsteuer": {
"account_number": "5790"
}
}
},
"Bezugsnebenkosten (Gruppe)": {
@ -2409,7 +2295,49 @@
},
"6 - sonstige betriebliche Ertr\u00e4ge": {
"root_type": "Income",
"is_group": 1,
"is_group": 1,
"Erhaltene Boni (Gruppe)": {
"is_group": 1,
"Erhaltene Boni 7 % Vorsteuer": {
"account_number": "5750"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
"account_number": "5753"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
"account_number": "5754"
},
"Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
"account_number": "5755"
},
"Erhaltene Boni 19 % Vorsteuer": {
"account_number": "5760"
},
"Erhaltene Boni": {
"account_number": "5769"
}
},
"Erhaltene Rabatte (Gruppe)": {
"is_group": 1,
"Erhaltene Rabatte": {
"account_number": "5770"
},
"Erhaltene Rabatte 7 % Vorsteuer": {
"account_number": "5780"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": {
"account_number": "5783"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": {
"account_number": "5784"
},
"Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": {
"account_number": "5785"
},
"Erhaltene Rabatte 19 % Vorsteuer": {
"account_number": "5790"
}
},
"Andere aktivierte Eigenleistungen": {
"account_number": "4820"
},
@ -2732,7 +2660,91 @@
},
"7 - sonstige betriebliche Aufwendungen": {
"root_type": "Expense",
"is_group": 1,
"is_group": 1,
"Erl\u00f6sschm\u00e4lerungen (Gruppe)": {
"is_group": 1,
"Erl\u00f6sschm\u00e4lerungen": {
"account_number": "4700"
},
"Erl\u00f6sschm\u00e4lerungen aus steuerfreien Ums\u00e4tzen \u00a7 4 Nr. 1a UStG": {
"account_number": "4705"
},
"Erl\u00f6sschm\u00e4lerungen 7 % USt": {
"account_number": "4710"
},
"Erl\u00f6sschm\u00e4lerungen 19 % USt": {
"account_number": "4720"
},
"Erl\u00f6sschm\u00e4lerungen 16 % USt": {
"account_number": "4723"
},
"Erl\u00f6sschm\u00e4lerungen aus steuerfreien innergem. Lieferungen": {
"account_number": "4724"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 7 % USt": {
"account_number": "4725"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 19 % USt": {
"account_number": "4726"
},
"Erl\u00f6sschm\u00e4lerungen aus im anderen EU-Land steuerpfl. Lieferungen": {
"account_number": "4727"
},
"Erl\u00f6sschm\u00e4lerungen aus im Inland steuerpfl. EU-Lieferungen 16 % USt": {
"account_number": "4729"
},
"Gew\u00e4hrte Skonti (Gruppe)": {
"is_group": 1,
"Gew. Skonti": {
"account_number": "4730"
},
"Gew. Skonti 7 % USt": {
"account_number": "4731"
},
"Gew. Skonti 19 % USt": {
"account_number": "4736"
},
"Gew. Skonti aus Lieferungen von Mobilfunkger./Schaltkr., f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4738"
},
"Gew. Skonti aus Leistungen, f. die der Leistungsempf. die Umsatzsteuer nach \u00a7 13b UStG schuldet": {
"account_number": "4741"
},
"Gew. Skonti aus Erl\u00f6sen aus im anderen EU-Land steuerpfl. Leistungen, f. die der Leistungsempf. die Ust. schuldet": {
"account_number": "4742"
},
"Gew. Skonti aus steuerfreien innergem. Lieferungen \u00a7 4 Nr. 1b UStG": {
"account_number": "4743"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen": {
"account_number": "4745"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 7% USt": {
"account_number": "4746"
},
"Gew. Skonti aus im Inland steuerpfl. EU-Lieferungen 19% USt": {
"account_number": "4748"
}
},
"Gew\u00e4hrte Boni 7 % USt": {
"account_number": "4750"
},
"Gew\u00e4hrte Boni 19 % USt": {
"account_number": "4760"
},
"Gew\u00e4hrte Boni": {
"account_number": "4769"
},
"Gew\u00e4hrte Rabatte": {
"account_number": "4770"
},
"Gew\u00e4hrte Rabatte 7 % USt": {
"account_number": "4780"
},
"Gew\u00e4hrte Rabatte 19 % USt": {
"account_number": "4790"
}
},
"Sonstige betriebliche Aufwendungen": {
"account_number": "6300"
},
@ -3609,18 +3621,6 @@
"Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen f. sonstige Steuern": {
"account_number": "7694"
}
},
"Debitoren": {
"root_type": "Asset",
"is_group": 1
},
"Kreditoren": {
"root_type": "Liability",
"is_group": 1,
"Wareneingangs-­Verrechnungskonto" : {
"account_number": "70001",
"account_type": "Stock Received But Not Billed"
}
}
}
}

View File

@ -304,8 +304,10 @@ class SalesInvoice(SellingController):
from erpnext.selling.doctype.customer.customer import check_credit_limit
validate_against_credit_limit = False
bypass_credit_limit_check_at_sales_order = cint(frappe.get_cached_value("Customer", self.customer,
"bypass_credit_limit_check_at_sales_order"))
bypass_credit_limit_check_at_sales_order = frappe.db.get_value("Customer Credit Limit",
filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
fieldname=["bypass_credit_limit_check"])
if bypass_credit_limit_check_at_sales_order:
validate_against_credit_limit = True

View File

@ -292,8 +292,11 @@ def validate_party_accounts(doc):
party_account_currency = frappe.db.get_value("Account", account.account, "account_currency", cache=True)
existing_gle_currency = get_party_gle_currency(doc.doctype, doc.name, account.company)
company_default_currency = frappe.get_cached_value('Company',
frappe.db.get_default("Company"), "default_currency")
if frappe.db.get_default("Company"):
company_default_currency = frappe.get_cached_value('Company',
frappe.db.get_default("Company"), "default_currency")
else:
company_default_currency = frappe.db.get_value('Company', account.company, "default_currency")
if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company))
@ -365,7 +368,7 @@ def validate_due_date(posting_date, due_date, party_type, party, company=None, b
.format(formatdate(default_due_date)))
@frappe.whitelist()
def get_address_tax_category(tax_category, billing_address=None, shipping_address=None):
def get_address_tax_category(tax_category=None, billing_address=None, shipping_address=None):
addr_tax_category_from = frappe.db.get_single_value("Accounts Settings", "determine_address_tax_category_from")
if addr_tax_category_from == "Shipping Address":
if shipping_address:
@ -607,4 +610,4 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None):
.format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
if data:
return frappe._dict(data)
return frappe._dict(data)

View File

@ -1,275 +1,269 @@
<style>
.print-format {
padding: 4mm;
font-size: 8.0pt !important;
}
.print-format td {
vertical-align:middle !important;
}
</style>
.print-format {
padding: 4mm;
font-size: 8.0pt !important;
}
.print-format td {
vertical-align:middle !important;
}
</style>
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% } %}
</h4>
<h6 class="text-center">
{% if (filters.tax_id) { %}
{%= __("Tax Id: ")%} {%= filters.tax_id %}
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% } %}
</h6>
<h5 class="text-center">
{%= __(filters.ageing_based_on) %}
{%= __("Until") %}
{%= frappe.datetime.str_to_user(filters.report_date) %}
</h5>
</h4>
<h6 class="text-center">
{% if (filters.tax_id) { %}
{%= __("Tax Id: ")%} {%= filters.tax_id %}
{% } %}
</h6>
<h5 class="text-center">
{%= __(filters.ageing_based_on) %}
{%= __("Until") %}
{%= frappe.datetime.str_to_user(filters.report_date) %}
</h5>
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) { %}
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
{% } %}
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) { %}
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
{% } %}
</div>
<div class="pull-right">
{% if(filters.credit_limit) { %}
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
{% } %}
</div>
</div>
<div class="pull-right">
{% if(filters.credit_limit) { %}
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
{% if(filters.show_future_payments) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
var range3 = report.columns[13].label;
var range4 = report.columns[14].label;
var range5 = report.columns[15].label;
%}
{% if(balance_row) { %}
<table class="table table-bordered table-condensed">
<caption class="text-right">(Amount in {%= data[0]["currency"] || "" %})</caption>
<colgroup>
<col style="width: 30mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
</colgroup>
<thead>
<tr>
<th>{%= __(" ") %}</th>
<th>{%= __(range1) %}</th>
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __(range5) %}</th>
<th>{%= __("Total") %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</td>
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
<td class="text-right">
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
</td>
</tr>
<td>{%= __("Future Payments") %}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
</td>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
</tbody>
</table>
{% } %}
{% } %}
</div>
</div>
{% if(filters.show_future_payments) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
var range3 = report.columns[13].label;
var range4 = report.columns[14].label;
var range5 = report.columns[15].label;
var range6 = report.columns[16].label;
%}
{% if(balance_row) { %}
<table class="table table-bordered table-condensed">
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
<colgroup>
<col style="width: 30mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
</colgroup>
<table class="table table-bordered">
<thead>
<tr>
<th>{%= __(" ") %}</th>
<th>{%= __(range1) %}</th>
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __(range5) %}</th>
<th>{%= __(range6) %}</th>
<th>{%= __("Total") %}</th>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("Future Payment Ref") %}</th>
<th style="width: 10%">{%= __("Future Payment Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
{% } %}
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</td>
<td class="text-right">{%= format_number(balance_row[range1], null, 2) %}</td>
<td class="text-right">{%= format_currency(balance_row[range2]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range5]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range6]) %}</td>
<td class="text-right">
{%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %}
</td>
</tr>
<td>{%= __("PDC/LC") %}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}
</td>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row[("outstanding_amount")]-balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
</tbody>
</table>
{% } %}
{% } %}
<table class="table table-bordered">
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
{% } %}
</tr>
</thead>
<tbody>
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
<td>
{% if(!filters.show_future_payments) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
{% if(data[i]["party"]) { %}
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
<td style="text-align: right">{%= data[i]["age"] %}</td>
<td>
{% if(!filters.show_future_payments) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
<div>
{% if data[i][__("Remarks")] %}
{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
{% } %}
</div>
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td></td>
{% if(!filters.show_future_payments) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td></td>
{% } %}
<td></td>
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| "&nbsp;") { %}
{% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %}
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
<br> {%= data[i]["supplier_name"] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
<div>
{% if data[i]["remarks"] %}
{%= __("Remarks") %}:
{%= data[i]["remarks"] %}
{% } %}
</div>
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
<td></td>
{% if(!filters.show_future_payments) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td></td>
{% } %}
<td></td>
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
{% if(data[i]["party"]|| "&nbsp;") { %}
{% if((data[i]["party"]) != __("'Total'")) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
<br> {%= data[i]["supplier_name"] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i]["remarks"] %}
</td>
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
{% } %}
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
{% } %}
</tr>
{% } %}
</tr>
{% } %}
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>

View File

@ -16,9 +16,9 @@ frappe.listview_settings['Purchase Order'] = {
return [__("To Receive"), "orange",
"per_received,<,100|per_billed,=,100|status,!=,Closed"];
}
} else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") {
} else if (flt(doc.per_received, 2) >= 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") {
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"];
} else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") {
} else if (flt(doc.per_received, 2) >= 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") {
return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"];
}
},

View File

@ -57,9 +57,9 @@ status_map = {
"Purchase Order": [
["Draft", None],
["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Bill", "eval:self.per_received == 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Bill", "eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1"],
["Delivered", "eval:self.status=='Delivered'"],
["Cancelled", "eval:self.docstatus==2"],
["On Hold", "eval:self.status=='On Hold'"],

View File

@ -39,7 +39,6 @@ def validate_filters(filters):
frappe.throw(_("'Based On' and 'Group By' can not be same"))
def get_data(filters, conditions):
data = []
inc, cond= '',''
query_details = conditions["based_on_select"] + conditions["period_wise_select"]
@ -47,10 +46,11 @@ def get_data(filters, conditions):
posting_date = 't1.transaction_date'
if conditions.get('trans') in ['Sales Invoice', 'Purchase Invoice', 'Purchase Receipt', 'Delivery Note']:
posting_date = 't1.posting_date'
if filters.period_based_on:
posting_date = 't1.'+filters.period_based_on
if conditions["based_on_select"] in ["t1.project,", "t2.project,"]:
cond = ' and '+ conditions["based_on_select"][:-1] +' IS Not NULL'
if conditions.get('trans') in ['Sales Order', 'Purchase Order']:
cond += " and t1.status != 'Closed'"

View File

@ -54,6 +54,7 @@ class Student(Document):
'send_welcome_email': 1,
'user_type': 'Website User'
})
student_user.flags.ignore_permissions = True
student_user.add_roles("Student")
student_user.save()
update_password_link = student_user.reset_password()

View File

@ -69,33 +69,10 @@ def validate_service_item(item, msg):
def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None):
fields = ["name", "first_name", "mobile_phone"]
match_conditions = build_match_conditions("Healthcare Practitioner")
match_conditions = "and {}".format(match_conditions) if match_conditions else ""
if filters:
filter_conditions = get_filters_cond(doctype, filters, [])
match_conditions += "{}".format(filter_conditions)
filters = {
'name': ("like", "%%%s%%" % txt)
}
return frappe.db.sql("""select %s from `tabHealthcare Practitioner` where docstatus < 2
and (%s like %s or first_name like %s)
and active = 1
{match_conditions}
order by
case when name like %s then 0 else 1 end,
case when first_name like %s then 0 else 1 end,
name, first_name limit %s, %s""".format(
match_conditions=match_conditions) %
(
", ".join(fields),
frappe.db.escape(searchfield),
"%s", "%s", "%s", "%s", "%s", "%s"
),
(
"%%%s%%" % frappe.db.escape(txt),
"%%%s%%" % frappe.db.escape(txt),
"%%%s%%" % frappe.db.escape(txt),
"%%%s%%" % frappe.db.escape(txt),
start,
page_len
)
)
return frappe.get_all("Healthcare Practitioner", fields = fields,
filters = filters, start=start, page_length=page_len, order_by="name, first_name", as_list=1)

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, cstr, getdate
from frappe.utils import cint, cstr, getdate, flt
import dateutil
from frappe.model.naming import set_name_by_naming_series
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms
@ -64,7 +64,7 @@ class Patient(Document):
def invoice_patient_registration(self):
frappe.db.set_value("Patient", self.name, "disabled", 0)
send_registration_sms(self)
if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0):
if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0):
company = frappe.defaults.get_user_default('company')
if not company:
company = frappe.db.get_value("Global Defaults", None, "default_company")

View File

@ -7,6 +7,7 @@
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"basic_information",
"employee",
@ -54,6 +55,7 @@
"column_break_44",
"holiday_list",
"default_shift",
"leave_approver",
"salary_information",
"salary_mode",
"bank_name",
@ -767,12 +769,18 @@
"fieldtype": "Link",
"label": "Default Shift",
"options": "Shift Type"
},
{
"fieldname": "leave_approver",
"fieldtype": "Link",
"label": "Leave Approver",
"options": "User"
}
],
"icon": "fa fa-user",
"idx": 24,
"image_field": "image",
"modified": "2019-06-01 16:05:55.132180",
"modified": "2019-09-06 15:54:36.735147",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@ -142,8 +142,10 @@ def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
elif check_in_out_type == 'Strictly based on Log Type in Employee Checkin':
if working_hours_calc_type == 'First Check-in and Last Check-out':
first_in_log = logs[find_index_in_dict(logs, 'log_type', 'IN')]
last_out_log = logs[len(logs)-1-find_index_in_dict(reversed(logs), 'log_type', 'OUT')]
first_in_log_index = find_index_in_dict(logs, 'log_type', 'IN')
first_in_log = logs[first_in_log_index] if first_in_log_index or first_in_log_index == 0 else None
last_out_log_index = find_index_in_dict(reversed(logs), 'log_type', 'OUT')
last_out_log = logs[len(logs)-1-last_out_log_index] if last_out_log_index or last_out_log_index == 0 else None
if first_in_log and last_out_log:
in_time, out_time = first_in_log.time, last_out_log.time
total_hours = time_diff_in_hours(in_time, out_time)

View File

@ -745,10 +745,12 @@ def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
return leave_days
@frappe.whitelist()
def get_leave_approver(employee, department=None):
if not department:
department = frappe.db.get_value('Employee', employee, 'department')
def get_leave_approver(employee):
leave_approver, department = frappe.db.get_value("Employee",
employee, ["leave_approver", "department"])
if department:
return frappe.db.get_value('Department Approver', {'parent': department,
'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
if not leave_approver and department:
leave_approver = frappe.db.get_value('Department Approver', {'parent': department,
'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
return leave_approver

View File

@ -1,648 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import json
from six import text_type
from frappe import _
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from frappe.utils.csvutils import getlink
from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
from erpnext.utilities.transaction_base import validate_uom_is_integer
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
class OperationTooLongError(frappe.ValidationError): pass
class ItemHasVariantError(frappe.ValidationError): pass
form_grid_templates = {
"operations": "templates/form_grid/production_order_grid.html"
}
class ProductionOrder(Document):
def validate(self):
self.validate_production_item()
if self.bom_no:
validate_bom_no(self.production_item, self.bom_no)
self.validate_sales_order()
self.set_default_warehouse()
self.validate_warehouse_belongs_to_company()
self.calculate_operating_cost()
self.validate_qty()
self.validate_operation_time()
self.status = self.get_status()
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
self.set_required_items(reset_only_qty = len(self.get("required_items")))
def validate_sales_order(self):
if self.sales_order:
so = frappe.db.sql("""
select so.name, so_item.delivery_date, so.project
from `tabSales Order` so
inner join `tabSales Order Item` so_item on so_item.parent = so.name
left join `tabProduct Bundle Item` pk_item on so_item.item_code = pk_item.parent
where so.name=%s and so.docstatus = 1 and (
so_item.item_code=%s or
pk_item.item_code=%s )
""", (self.sales_order, self.production_item, self.production_item), as_dict=1)
if not so:
so = frappe.db.sql("""
select
so.name, so_item.delivery_date, so.project
from
`tabSales Order` so, `tabSales Order Item` so_item, `tabPacked Item` packed_item
where so.name=%s
and so.name=so_item.parent
and so.name=packed_item.parent
and so_item.item_code = packed_item.parent_item
and so.docstatus = 1 and packed_item.item_code=%s
""", (self.sales_order, self.production_item), as_dict=1)
if len(so):
if not self.expected_delivery_date:
self.expected_delivery_date = so[0].delivery_date
if so[0].project:
self.project = so[0].project
if not self.material_request:
self.validate_production_order_against_so()
else:
frappe.throw(_("Sales Order {0} is not valid").format(self.sales_order))
def set_default_warehouse(self):
if not self.wip_warehouse:
self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
if not self.fg_warehouse:
self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
def validate_warehouse_belongs_to_company(self):
warehouses = [self.fg_warehouse, self.wip_warehouse]
for d in self.get("required_items"):
if d.source_warehouse not in warehouses:
warehouses.append(d.source_warehouse)
for wh in warehouses:
validate_warehouse_company(wh, self.company)
def calculate_operating_cost(self):
self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0
for d in self.get("operations"):
d.planned_operating_cost = flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0)
d.actual_operating_cost = flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0)
self.planned_operating_cost += flt(d.planned_operating_cost)
self.actual_operating_cost += flt(d.actual_operating_cost)
variable_cost = self.actual_operating_cost if self.actual_operating_cost \
else self.planned_operating_cost
self.total_operating_cost = flt(self.additional_operating_cost) + flt(variable_cost)
def validate_production_order_against_so(self):
# already ordered qty
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order`
where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""",
(self.production_item, self.sales_order, self.name))[0][0]
total_qty = flt(ordered_qty_against_so) + flt(self.qty)
# get qty from Sales Order Item table
so_item_qty = frappe.db.sql("""select sum(stock_qty) from `tabSales Order Item`
where parent = %s and item_code = %s""",
(self.sales_order, self.production_item))[0][0]
# get qty from Packing Item table
dnpi_qty = frappe.db.sql("""select sum(qty) from `tabPacked Item`
where parent = %s and parenttype = 'Sales Order' and item_code = %s""",
(self.sales_order, self.production_item))[0][0]
# total qty in SO
so_qty = flt(so_item_qty) + flt(dnpi_qty)
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"over_production_allowance_percentage"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
.format(self.production_item, so_qty), OverProductionError)
def update_status(self, status=None):
'''Update status of production order if unknown'''
if status != "Stopped":
status = self.get_status(status)
if status != self.status:
self.db_set("status", status)
self.update_required_items()
return status
def get_status(self, status=None):
'''Return the status based on stock entries against this production order'''
if not status:
status = self.status
if self.docstatus==0:
status = 'Draft'
elif self.docstatus==1:
if status != 'Stopped':
stock_entries = frappe._dict(frappe.db.sql("""select purpose, sum(fg_completed_qty)
from `tabStock Entry` where production_order=%s and docstatus=1
group by purpose""", self.name))
status = "Not Started"
if stock_entries:
status = "In Process"
produced_qty = stock_entries.get("Manufacture")
if flt(produced_qty) == flt(self.qty):
status = "Completed"
else:
status = 'Cancelled'
return status
def update_production_order_qty(self):
"""Update **Manufactured Qty** and **Material Transferred for Qty** in Production Order
based on Stock Entry"""
for purpose, fieldname in (("Manufacture", "produced_qty"),
("Material Transfer for Manufacture", "material_transferred_for_manufacturing")):
qty = flt(frappe.db.sql("""select sum(fg_completed_qty)
from `tabStock Entry` where production_order=%s and docstatus=1
and purpose=%s""", (self.name, purpose))[0][0])
if qty > self.qty:
frappe.throw(_("{0} ({1}) cannot be greater than planned quanitity ({2}) in Production Order {3}").format(\
self.meta.get_label(fieldname), qty, self.qty, self.name), StockOverProductionError)
self.db_set(fieldname, qty)
def before_submit(self):
self.make_time_logs()
def on_submit(self):
if not self.wip_warehouse:
frappe.throw(_("Work-in-Progress Warehouse is required before Submit"))
if not self.fg_warehouse:
frappe.throw(_("For Warehouse is required before Submit"))
self.update_reserved_qty_for_production()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
def on_cancel(self):
self.validate_cancel()
frappe.db.set(self,'status', 'Cancelled')
self.delete_timesheet()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
self.update_reserved_qty_for_production()
def validate_cancel(self):
if self.status == "Stopped":
frappe.throw(_("Stopped Production Order cannot be cancelled, Unstop it first to cancel"))
# Check whether any stock entry exists against this Production Order
stock_entry = frappe.db.sql("""select name from `tabStock Entry`
where production_order = %s and docstatus = 1""", self.name)
if stock_entry:
frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0]))
def update_planned_qty(self):
update_bin_qty(self.production_item, self.fg_warehouse, {
"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)
})
if self.material_request:
mr_obj = frappe.get_doc("Material Request", self.material_request)
mr_obj.update_requested_qty([self.material_request_item])
def update_completed_qty_in_material_request(self):
if self.material_request:
frappe.get_doc("Material Request", self.material_request).update_completed_qty([self.material_request_item])
def set_production_order_operations(self):
"""Fetch operations from BOM and set in 'Production Order'"""
self.set('operations', [])
if not self.bom_no \
or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
return
if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
else:
bom_list = [self.bom_no]
operations = frappe.db.sql("""
select
operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins,
"Pending" as status, parent as bom
from
`tabBOM Operation`
where
parent in (%s) order by idx
""" % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
self.set('operations', operations)
self.calculate_time()
def calculate_time(self):
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
for d in self.get("operations"):
d.time_in_mins = flt(d.time_in_mins) / flt(bom_qty) * flt(self.qty)
self.calculate_operating_cost()
def get_holidays(self, workstation):
holiday_list = frappe.db.get_value("Workstation", workstation, "holiday_list")
holidays = {}
if holiday_list not in holidays:
holiday_list_days = [getdate(d[0]) for d in frappe.get_all("Holiday", fields=["holiday_date"],
filters={"parent": holiday_list}, order_by="holiday_date", limit_page_length=0, as_list=1)]
holidays[holiday_list] = holiday_list_days
return holidays[holiday_list]
def make_time_logs(self, open_new=False):
"""Capacity Planning. Plan time logs based on earliest availablity of workstation after
Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted
before manufacturing entry can be made."""
if not self.operations:
return
timesheets = []
plan_days = frappe.db.get_single_value("Manufacturing Settings", "capacity_planning_for_days") or 30
timesheet = make_timesheet(self.name, self.company)
timesheet.set('time_logs', [])
for i, d in enumerate(self.operations):
if d.status != 'Completed':
self.set_start_end_time_for_workstation(d, i)
args = self.get_operations_data(d)
add_timesheet_detail(timesheet, args)
original_start_time = d.planned_start_time
# validate operating hours if workstation [not mandatory] is specified
try:
timesheet.validate_time_logs()
except OverlapError:
if frappe.message_log: frappe.message_log.pop()
timesheet.schedule_for_production_order(d.idx)
except WorkstationHolidayError:
if frappe.message_log: frappe.message_log.pop()
timesheet.schedule_for_production_order(d.idx)
from_time, to_time = self.get_start_end_time(timesheet, d.name)
if date_diff(from_time, original_start_time) > cint(plan_days):
frappe.throw(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
break
d.planned_start_time = from_time
d.planned_end_time = to_time
d.db_update()
if timesheet and open_new:
return timesheet
if timesheet and timesheet.get("time_logs"):
timesheet.save()
timesheets.append(getlink("Timesheet", timesheet.name))
self.planned_end_date = self.operations[-1].planned_end_time
if timesheets:
frappe.local.message_log = []
frappe.msgprint(_("Timesheet created:") + "\n" + "\n".join(timesheets))
def get_operations_data(self, data):
return {
'from_time': get_datetime(data.planned_start_time),
'hours': data.time_in_mins / 60.0,
'to_time': get_datetime(data.planned_end_time),
'project': self.project,
'operation': data.operation,
'operation_id': data.name,
'workstation': data.workstation,
'completed_qty': flt(self.qty) - flt(data.completed_qty)
}
def set_start_end_time_for_workstation(self, data, index):
"""Set start and end time for given operation. If first operation, set start as
`planned_start_date`, else add time diff to end time of earlier operation."""
if index == 0:
data.planned_start_time = self.planned_start_date
else:
data.planned_start_time = get_datetime(self.operations[index-1].planned_end_time)\
+ get_mins_between_operations()
data.planned_end_time = get_datetime(data.planned_start_time) + relativedelta(minutes = data.time_in_mins)
if data.planned_start_time == data.planned_end_time:
frappe.throw(_("Capacity Planning Error"))
def get_start_end_time(self, timesheet, operation_id):
for data in timesheet.time_logs:
if data.operation_id == operation_id:
return data.from_time, data.to_time
def check_operation_fits_in_working_hours(self, d):
"""Raises expection if operation is longer than working hours in the given workstation."""
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
check_if_within_operating_hours(d.workstation, d.operation, d.planned_start_time, d.planned_end_time)
def update_operation_status(self):
for d in self.get("operations"):
if not d.completed_qty:
d.status = "Pending"
elif flt(d.completed_qty) < flt(self.qty):
d.status = "Work in Progress"
elif flt(d.completed_qty) == flt(self.qty):
d.status = "Completed"
else:
frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'"))
def set_actual_dates(self):
self.actual_start_date = None
self.actual_end_date = None
if self.get("operations"):
actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time]
if actual_start_dates:
self.actual_start_date = min(actual_start_dates)
actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time]
if actual_end_dates:
self.actual_end_date = max(actual_end_dates)
def delete_timesheet(self):
for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}):
frappe.delete_doc("Timesheet", timesheet.name)
def validate_production_item(self):
if frappe.db.get_value("Item", self.production_item, "has_variants"):
frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError)
if self.production_item:
validate_end_of_life(self.production_item)
def validate_qty(self):
if not self.qty > 0:
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
def validate_operation_time(self):
for d in self.operations:
if not d.time_in_mins > 0:
frappe.throw(_("Operation Time must be greater than 0 for Operation {0}".format(d.operation)))
def update_required_items(self):
'''
update bin reserved_qty_for_production
called from Stock Entry for production, after submit, cancel
'''
if self.docstatus==1:
# calculate transferred qty based on submitted stock entries
self.update_transaferred_qty_for_required_items()
# update in bin
self.update_reserved_qty_for_production()
def update_reserved_qty_for_production(self, items=None):
'''update reserved_qty_for_production in bins'''
for d in self.required_items:
if d.source_warehouse:
stock_bin = get_bin(d.item_code, d.source_warehouse)
stock_bin.update_reserved_qty_for_production()
def get_items_and_operations_from_bom(self):
self.set_required_items()
self.set_production_order_operations()
return check_if_scrap_warehouse_mandatory(self.bom_no)
def set_available_qty(self):
for d in self.get("required_items"):
if d.source_warehouse:
d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse)
if self.wip_warehouse:
d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse)
def set_required_items(self, reset_only_qty=False):
'''set required_items for production to keep track of reserved qty'''
if not reset_only_qty:
self.required_items = []
if self.bom_no and self.qty:
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=self.qty,
fetch_exploded = self.use_multi_level_bom)
if reset_only_qty:
for d in self.get("required_items"):
if item_dict.get(d.item_code):
d.required_qty = item_dict.get(d.item_code).get("qty")
else:
for item in sorted(item_dict.values(), key=lambda d: d['idx']):
self.append('required_items', {
'item_code': item.item_code,
'item_name': item.item_name,
'description': item.description,
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse
})
self.set_available_qty()
def update_transaferred_qty_for_required_items(self):
'''update transferred qty from submitted stock entries for that item against
the production order'''
for d in self.required_items:
transferred_qty = frappe.db.sql('''select sum(qty)
from `tabStock Entry` entry, `tabStock Entry Detail` detail
where
entry.production_order = %s
and entry.purpose = "Material Transfer for Manufacture"
and entry.docstatus = 1
and detail.parent = entry.name
and detail.item_code = %s''', (self.name, d.item_code))[0][0]
d.db_set('transferred_qty', flt(transferred_qty), update_modified = False)
@frappe.whitelist()
def get_item_details(item, project = None):
res = frappe.db.sql("""
select stock_uom, description
from `tabItem`
where disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
and name=%s
""", (nowdate(), item), as_dict=1)
if not res:
return {}
res = res[0]
filters = {"item": item, "is_default": 1}
if project:
filters = {"item": item, "project": project}
res["bom_no"] = frappe.db.get_value("BOM", filters = filters)
if not res["bom_no"]:
variant_of= frappe.db.get_value("Item", item, "variant_of")
if variant_of:
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1})
if not res["bom_no"]:
if project:
res = get_item_details(item)
frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1)
else:
frappe.throw(_("Default BOM for {0} not found").format(item))
res['project'] = project or frappe.db.get_value('BOM', res['bom_no'], 'project')
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
return res
@frappe.whitelist()
def check_if_scrap_warehouse_mandatory(bom_no):
res = {"set_scrap_wh_mandatory": False }
if bom_no:
bom = frappe.get_doc("BOM", bom_no)
if len(bom.scrap_items) > 0:
res["set_scrap_wh_mandatory"] = True
return res
@frappe.whitelist()
def set_production_order_ops(name):
po = frappe.get_doc('Production Order', name)
po.set_production_order_operations()
po.save()
@frappe.whitelist()
def make_stock_entry(production_order_id, purpose, qty=None):
production_order = frappe.get_doc("Production Order", production_order_id)
if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group") \
and not production_order.skip_transfer:
wip_warehouse = production_order.wip_warehouse
else:
wip_warehouse = None
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = purpose
stock_entry.production_order = production_order_id
stock_entry.company = production_order.company
stock_entry.from_bom = 1
stock_entry.bom_no = production_order.bom_no
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
stock_entry.set_stock_entry_type()
if purpose=="Material Transfer for Manufacture":
stock_entry.to_warehouse = wip_warehouse
stock_entry.project = production_order.project
else:
stock_entry.from_warehouse = wip_warehouse
stock_entry.to_warehouse = production_order.fg_warehouse
additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty)
stock_entry.project = production_order.project
stock_entry.set("additional_costs", additional_costs)
stock_entry.get_items()
return stock_entry.as_dict()
@frappe.whitelist()
def make_timesheet(production_order, company):
timesheet = frappe.new_doc("Timesheet")
timesheet.employee = ""
timesheet.production_order = production_order
timesheet.company = company
return timesheet
@frappe.whitelist()
def add_timesheet_detail(timesheet, args):
if isinstance(timesheet, text_type):
timesheet = frappe.get_doc('Timesheet', timesheet)
if isinstance(args, text_type):
args = json.loads(args)
timesheet.append('time_logs', args)
return timesheet
@frappe.whitelist()
def get_default_warehouse():
wip_warehouse = frappe.db.get_single_value("Manufacturing Settings",
"default_wip_warehouse")
fg_warehouse = frappe.db.get_single_value("Manufacturing Settings",
"default_fg_warehouse")
return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse}
@frappe.whitelist()
def make_new_timesheet(source_name, target_doc=None):
po = frappe.get_doc('Production Order', source_name)
ts = po.make_time_logs(open_new=True)
if not ts or not ts.get('time_logs'):
frappe.throw(_("Already completed"))
return ts
@frappe.whitelist()
def stop_unstop(production_order, status):
""" Called from client side on Stop/Unstop event"""
if not frappe.has_permission("Production Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
pro_order = frappe.get_doc("Production Order", production_order)
pro_order.update_status(status)
pro_order.update_planned_qty()
frappe.msgprint(_("Production Order has been {0}").format(status))
pro_order.notify_update()
return pro_order.status
@frappe.whitelist()
def query_sales_order(production_item):
out = frappe.db.sql_list("""
select distinct so.name from `tabSales Order` so, `tabSales Order Item` so_item
where so_item.parent=so.name and so_item.item_code=%s and so.docstatus=1
union
select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item
where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1
""", (production_item, production_item))
return out

View File

@ -99,7 +99,7 @@ class ProductionPlan(Document):
self.get_mr_items()
def get_so_items(self):
so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
if not so_list:
msgprint(_("Please enter Sales Orders in the above table"))
return []
@ -134,7 +134,7 @@ class ProductionPlan(Document):
self.calculate_total_planned_qty()
def get_mr_items(self):
mr_list = [d.material_request for d in self.material_requests if d.material_request]
mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
if not mr_list:
msgprint(_("Please enter Material Requests in the above table"))
return []

View File

@ -632,3 +632,4 @@ execute:frappe.reload_doc('desk', 'doctype','dashboard_chart')
erpnext.patches.v12_0.add_default_dashboards
erpnext.patches.v12_0.remove_bank_remittance_custom_fields
erpnext.patches.v12_0.generate_leave_ledger_entries
erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit

View File

@ -0,0 +1,46 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
''' Move credit limit and bypass credit limit to the child table of customer credit limit '''
frappe.reload_doc("Selling", "doctype", "Customer Credit Limit")
frappe.reload_doc("Selling", "doctype", "Customer")
frappe.reload_doc("Setup", "doctype", "Customer Group")
if frappe.db.a_row_exists("Customer Credit Limit"):
return
move_credit_limit_to_child_table()
def move_credit_limit_to_child_table():
''' maps data from old field to the new field in the child table '''
companies = frappe.get_all("Company", 'name')
for doctype in ("Customer", "Customer Group"):
fields = ""
if doctype == "Customer" \
and frappe.db.has_column('Customer', 'bypass_credit_limit_check_at_sales_order'):
fields = ", bypass_credit_limit_check_at_sales_order"
credit_limit_records = frappe.db.sql('''
SELECT name, credit_limit {0}
FROM `tab{1}` where credit_limit > 0
'''.format(fields, doctype), as_dict=1) #nosec
for record in credit_limit_records:
doc = frappe.get_doc(doctype, record.name)
for company in companies:
row = frappe._dict({
'credit_limit': record.credit_limit,
'company': company.name
})
if doctype == "Customer":
row.bypass_credit_limit_check = record.bypass_credit_limit_check_at_sales_order
doc.append("credit_limits", row)
for row in doc.credit_limits:
row.db_insert()

View File

@ -82,7 +82,7 @@ def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_
account_name = " - ".join(parts[:-1])
company = frappe.db.get_value("Company", filters={"abbr": parts[-1]})
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0}, fieldname="parent_account")
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
frappe.get_doc({
"doctype": "Account",

View File

@ -147,6 +147,15 @@ frappe.ui.form.on("Timesheet Detail", {
calculate_time_and_amount(frm);
},
task: (frm, cdt, cdn) => {
let row = frm.selected_doc;
if (row.task) {
frappe.db.get_value("Task", row.task, "project", (r) => {
frappe.model.set_value(cdt, cdn, "project", r.project);
});
}
},
from_time: function(frm, cdt, cdn) {
calculate_end_time(frm, cdt, cdn);
},
@ -200,9 +209,6 @@ frappe.ui.form.on("Timesheet Detail", {
},
activity_type: function(frm, cdt, cdn) {
frm.script_manager.copy_from_first_row('time_logs', frm.selected_doc,
'project');
frappe.call({
method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
args: {

View File

@ -145,12 +145,17 @@ class Timesheet(Document):
def validate_time_logs(self):
for data in self.get('time_logs'):
self.validate_overlap(data)
self.validate_task_project()
def validate_overlap(self, data):
settings = frappe.get_single('Projects Settings')
self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
def validate_task_project(self):
for log in self.time_logs:
log.project = log.project or frappe.db.get_value("Task", log.task, "project")
def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
if not value or ignore_validation:
return

View File

@ -3,6 +3,14 @@
erpnext.get_purchase_trends_filters = function() {
return [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("Company")
},
{
"fieldname":"period",
"label": __("Period"),
@ -15,6 +23,23 @@ erpnext.get_purchase_trends_filters = function() {
],
"default": "Monthly"
},
{
"fieldname":"fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options":'Fiscal Year',
"default": frappe.sys_defaults.fiscal_year
},
{
"fieldname":"period_based_on",
"label": __("Period based On"),
"fieldtype": "Select",
"options": [
{ "value": "posting_date", "label": __("Posting Date") },
{ "value": "bill_date", "label": __("Billing Date") },
],
"default": "posting_date"
},
{
"fieldname":"based_on",
"label": __("Based On"),
@ -39,19 +64,5 @@ erpnext.get_purchase_trends_filters = function() {
],
"default": ""
},
{
"fieldname":"fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options":'Fiscal Year',
"default": frappe.sys_defaults.fiscal_year
},
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company")
},
];
}

View File

@ -52,7 +52,19 @@ class QualityProcedure(NestedSet):
def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
if parent is None or parent == "All Quality Procedures":
parent = ""
return frappe.get_all(doctype, fields=["name as value", "is_group as expandable"], filters={"parent_quality_procedure": parent})
return frappe.db.sql("""
select
name as value,
is_group as expandable
from
`tab{doctype}`
where
ifnull(parent_quality_procedure, "")={parent}
""".format(
doctype = doctype,
parent=frappe.db.escape(parent)
), as_dict=1)
@frappe.whitelist()
def add_node():

View File

@ -1,5 +1,3 @@
frappe.provide("frappe.treeview_settings");
frappe.treeview_settings["Quality Procedure"] = {
ignore_fields:["parent_quality_procedure"],
get_tree_nodes: 'erpnext.quality_management.doctype.quality_procedure.quality_procedure.get_children',
@ -19,7 +17,7 @@ frappe.treeview_settings["Quality Procedure"] = {
],
breadcrumb: "Setup",
root_label: "All Quality Procedures",
get_tree_root: true,
get_tree_root: false,
menu_items: [
{
label: __("New Quality Procedure"),

File diff suppressed because it is too large Load Diff

View File

@ -167,13 +167,18 @@ class Customer(TransactionBase):
frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"), frappe.NameError)
def validate_credit_limit_on_change(self):
if self.get("__islocal") or not self.credit_limit \
or self.credit_limit == frappe.db.get_value("Customer", self.name, "credit_limit"):
if self.get("__islocal") or not self.credit_limits:
return
for company in frappe.get_all("Company"):
outstanding_amt = get_customer_outstanding(self.name, company.name)
if flt(self.credit_limit) < outstanding_amt:
company_record = []
for limit in self.credit_limits:
if limit.company in company_record:
frappe.throw(_("Credit limit is already defined for the Company {0}").format(limit.company, self.name))
else:
company_record.append(limit.company)
outstanding_amt = get_customer_outstanding(self.name, limit.company)
if flt(limit.credit_limit) < outstanding_amt:
frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt))
def on_trash(self):
@ -322,11 +327,13 @@ def get_credit_limit(customer, company):
credit_limit = None
if customer:
credit_limit, customer_group = frappe.get_cached_value("Customer",
customer, ["credit_limit", "customer_group"])
credit_limit = frappe.db.get_value("Customer Credit Limit",
{'parent': customer, 'parenttype': 'Customer', 'company': company}, 'credit_limit')
if not credit_limit:
credit_limit = frappe.get_cached_value("Customer Group", customer_group, "credit_limit")
customer_group = frappe.get_cached_value("Customer", customer, 'customer_group')
credit_limit = frappe.db.get_value("Customer Credit Limit",
{'parent': customer_group, 'parenttype': 'Customer Group', 'company': company}, 'credit_limit')
if not credit_limit:
credit_limit = frappe.get_cached_value('Company', company, "credit_limit")

View File

@ -9,7 +9,7 @@ def get_data():
'heatmap_message': _('This is based on transactions against this Customer. See timeline below for details'),
'fieldname': 'customer',
'non_standard_fieldnames': {
'Payment Entry': 'party_name',
'Payment Entry': 'party',
'Quotation': 'party_name',
'Opportunity': 'party_name'
},

View File

@ -25,7 +25,7 @@ class TestCustomer(unittest.TestCase):
make_test_records('Item')
def tearDown(self):
frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', 0.0)
set_credit_limit('_Test Customer', '_Test Company', 0)
def test_party_details(self):
from erpnext.accounts.party import get_party_details
@ -225,8 +225,8 @@ class TestCustomer(unittest.TestCase):
item_qty = int((abs(outstanding_amt) + 200)/100)
make_sales_order(qty=item_qty)
if credit_limit == 0.0:
frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', outstanding_amt - 50.0)
if not credit_limit:
set_credit_limit('_Test Customer', '_Test Company', outstanding_amt - 50)
# Sales Order
so = make_sales_order(do_not_submit=True)
@ -241,7 +241,7 @@ class TestCustomer(unittest.TestCase):
self.assertRaises(frappe.ValidationError, si.submit)
if credit_limit > outstanding_amt:
frappe.db.set_value("Customer", '_Test Customer', 'credit_limit', credit_limit)
set_credit_limit('_Test Customer', '_Test Company', credit_limit)
# Makes Sales invoice from Sales Order
so.save(ignore_permissions=True)
@ -252,7 +252,10 @@ class TestCustomer(unittest.TestCase):
def test_customer_credit_limit_on_change(self):
outstanding_amt = self.get_customer_outstanding_amount()
customer = frappe.get_doc("Customer", '_Test Customer')
customer.credit_limit = flt(outstanding_amt - 100)
customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'})
''' define new credit limit for same company '''
customer.append('credit_limits', {'credit_limit': flt(outstanding_amt - 100), 'company': '_Test Company'})
self.assertRaises(frappe.ValidationError, customer.save)
def test_customer_payment_terms(self):
@ -292,3 +295,20 @@ def get_customer_dict(customer_name):
"doctype": "Customer",
"territory": "_Test Territory"
}
def set_credit_limit(customer, company, credit_limit):
customer = frappe.get_doc("Customer", customer)
existing_row = None
for d in customer.credit_limits:
if d.company == company:
existing_row = d
d.credit_limit = credit_limit
d.db_update()
break
if not existing_row:
customer.append('credit_limits', {
'company': company,
'credit_limit': credit_limit
})
customer.credit_limits[-1].db_insert()

View File

@ -0,0 +1,50 @@
{
"creation": "2019-08-28 17:29:42.115592",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"column_break_2",
"credit_limit",
"bypass_credit_limit_check"
],
"fields": [
{
"columns": 4,
"fieldname": "credit_limit",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Credit Limit"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"columns": 4,
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company"
},
{
"default": "0",
"fieldname": "bypass_credit_limit_check",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Bypass credit limit_check"
}
],
"istable": 1,
"modified": "2019-08-29 20:46:36.073953",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer Credit Limit",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class CustomerCreditLimit(Document):
pass

View File

@ -208,7 +208,9 @@ class SalesOrder(SellingController):
def check_credit_limit(self):
# if bypass credit limit check is set to true (1) at sales order level,
# then we need not to check credit limit and vise versa
if not cint(frappe.get_cached_value("Customer", self.customer, "bypass_credit_limit_check_at_sales_order")):
if not cint(frappe.db.get_value("Customer Credit Limit",
{'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
"bypass_credit_limit_check")):
check_credit_limit(self.customer, self.company)
def check_nextdoc_docstatus(self):
@ -518,7 +520,7 @@ def make_material_request(source_name, target_doc=None):
"doctype": "Material Request Item",
"field_map": {
"parent": "sales_order",
"stock_uom": "uom"
"uom": "stock_uom"
},
"postprocess": update_item
},

View File

@ -1,7 +1,7 @@
QUnit.module('Sales Order');
QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) {
//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
assert.expect(2);
let done = assert.async();
frappe.run_serially([
@ -10,8 +10,10 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) {
() => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
() => frappe.timeout(1),
() => cur_frm.set_value("customer_name", "Test Customer 10"),
() => cur_frm.set_value("credit_limit", 100.00),
() => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 1),
() => cur_frm.add_child('credit_limits', {
'company': cur_frm.doc.company || '_Test Company'
'credit_limit': 1000,
'bypass_credit_limit_check': 1}),
// save form
() => cur_frm.save(),
() => frappe.timeout(1),
@ -22,10 +24,10 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) {
() => frappe.timeout(1),
() => cur_frm.set_value("item_code", "Test Product 10"),
() => cur_frm.set_value("item_group", "Products"),
() => cur_frm.set_value("standard_rate", 100),
() => cur_frm.set_value("standard_rate", 100),
// save form
() => cur_frm.save(),
() => frappe.timeout(1),
() => frappe.timeout(1),
() => {
return frappe.tests.make('Sales Order', [
@ -46,11 +48,11 @@ QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) {
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => {
assert.ok(cur_frm.doc.status=="To Deliver and Bill", "It is submited. Credit limit is NOT checked for sales order");
},
},
() => done()
]);
});

View File

@ -1,7 +1,7 @@
QUnit.module('Sales Order');
QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert) {
//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com
assert.expect(2);
let done = assert.async();
frappe.run_serially([
@ -10,8 +10,10 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert
() => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(),
() => frappe.timeout(1),
() => cur_frm.set_value("customer_name", "Test Customer 11"),
() => cur_frm.set_value("credit_limit", 100.00),
() => cur_frm.set_value("bypass_credit_limit_check_at_sales_order", 0),
() => cur_frm.add_child('credit_limits', {
'credit_limit': 1000,
'company': '_Test Company',
'bypass_credit_limit_check': 1}),
// save form
() => cur_frm.save(),
() => frappe.timeout(1),
@ -21,10 +23,10 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert
() => frappe.click_link('Edit in full page'),
() => cur_frm.set_value("item_code", "Test Product 11"),
() => cur_frm.set_value("item_group", "Products"),
() => cur_frm.set_value("standard_rate", 100),
() => cur_frm.set_value("standard_rate", 100),
// save form
() => cur_frm.save(),
() => frappe.timeout(1),
() => frappe.timeout(1),
() => {
return frappe.tests.make('Sales Order', [
@ -45,14 +47,14 @@ QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => {
if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/))
{
if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/))
{
/*Match found */
assert.ok(true, "Credit Limit crossed message received");
}
},
() => cur_dialog.cancel(),
() => done()

View File

@ -29,7 +29,7 @@ def execute(filters=None):
if customer_naming_type == "Naming Series":
row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal,
d.bypass_credit_limit_check_at_sales_order, d.is_frozen,
d.bypass_credit_limit_check, d.is_frozen,
d.disabled]
else:
row = [d.name, credit_limit, outstanding_amt, bal,
@ -60,9 +60,15 @@ def get_details(filters):
conditions = ""
if filters.get("customer"):
conditions += " where name = %(customer)s"
return frappe.db.sql("""select name, customer_name,
bypass_credit_limit_check_at_sales_order, is_frozen, disabled from `tabCustomer` %s
""" % conditions, filters, as_dict=1)
conditions += " AND c.name = " + filters.get("customer")
return frappe.db.sql("""SELECT
c.name, c.customer_name,
ccl.bypass_credit_limit_check,
c.is_frozen, c.disabled
FROM `tabCustomer` c, `tabCustomer Credit Limit` ccl
WHERE
c.name = ccl.parent
AND ccl.company = %s
{0}
""".format(conditions), (filters.get("company")), as_dict=1) #nosec

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import cint,cstr
from frappe.utils import flt
def execute(filters=None):
columns = get_columns()
@ -100,7 +100,7 @@ def get_data():
so.transaction_date,
so.customer,
so.territory,
sum(so_item.qty) as net_qty,
sum(so_item.qty) as total_qty,
so.company
FROM `tabSales Order` so, `tabSales Order Item` so_item
WHERE
@ -111,10 +111,15 @@ def get_data():
so.name,so_item.item_code
""", as_dict = 1)
sales_orders = [row.name for row in sales_order_entry]
mr_records = frappe.get_all("Material Request Item",
{"sales_order_item": ("!=",""), "docstatus": 1},
{"sales_order": ("in", sales_orders), "docstatus": 1},
["parent", "qty", "sales_order", "item_code"])
bundled_item_map = get_packed_items(sales_orders)
item_with_product_bundle = get_items_with_product_bundle([row.item_code for row in sales_order_entry])
materials_request_dict = {}
for record in mr_records:
@ -131,27 +136,64 @@ def get_data():
if record.parent not in details.get('material_requests'):
details['material_requests'].append(record.parent)
pending_so=[]
pending_so = []
for so in sales_order_entry:
# fetch all the material request records for a sales order item
key = (so.name, so.item_code)
materials_request = materials_request_dict.get(key) or {}
if so.item_code not in item_with_product_bundle:
material_requests_against_so = materials_request_dict.get((so.name, so.item_code)) or {}
# check for pending sales order
if flt(so.total_qty) > flt(material_requests_against_so.get('qty')):
so_record = {
"item_code": so.item_code,
"item_name": so.item_name,
"description": so.description,
"sales_order_no": so.name,
"date": so.transaction_date,
"material_request": ','.join(material_requests_against_so.get('material_requests', [])),
"customer": so.customer,
"territory": so.territory,
"so_qty": so.total_qty,
"requested_qty": material_requests_against_so.get('qty'),
"pending_qty": so.total_qty - flt(material_requests_against_so.get('qty')),
"company": so.company
}
pending_so.append(so_record)
else:
for item in bundled_item_map.get((so.name, so.item_code)):
material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {}
if flt(item.qty) > flt(material_requests_against_so.get('qty')):
so_record = {
"item_code": item.item_code,
"item_name": item.item_name,
"description": item.description,
"sales_order_no": so.name,
"date": so.transaction_date,
"material_request": ','.join(material_requests_against_so.get('material_requests', [])),
"customer": so.customer,
"territory": so.territory,
"so_qty": item.qty,
"requested_qty": material_requests_against_so.get('qty', 0),
"pending_qty": item.qty - flt(material_requests_against_so.get('qty', 0)),
"company": so.company
}
pending_so.append(so_record)
# check for pending sales order
if cint(so.net_qty) > cint(materials_request.get('qty')):
so_record = {
"item_code": so.item_code,
"item_name": so.item_name,
"description": so.description,
"sales_order_no": so.name,
"date": so.transaction_date,
"material_request": ','.join(materials_request.get('material_requests', [])),
"customer": so.customer,
"territory": so.territory,
"so_qty": so.net_qty,
"requested_qty": cint(materials_request.get('qty')),
"pending_qty": so.net_qty - cint(materials_request.get('qty')),
"company": so.company
}
pending_so.append(so_record)
return pending_so
return pending_so
def get_items_with_product_bundle(item_list):
bundled_items = frappe.get_all("Product Bundle", filters = [
("new_item_code", "IN", item_list)
], fields = ["new_item_code"])
return [d.new_item_code for d in bundled_items]
def get_packed_items(sales_order_list):
packed_items = frappe.get_all("Packed Item", filters = [
("parent", "IN", sales_order_list)
], fields = ["parent_item", "item_code", "qty", "item_name", "description", "parent"])
bundled_item_map = frappe._dict()
for d in packed_items:
bundled_item_map.setdefault((d.parent, d.parent_item), []).append(d)
return bundled_item_map

View File

@ -40,6 +40,16 @@ class Analytics(object):
"fieldtype": "Data",
"width": 140
})
if self.filters.tree_type == "Item":
self.columns.append({
"label": _("UOM"),
"fieldname": 'stock_uom',
"fieldtype": "Link",
"options": "UOM",
"width": 100
})
for end_date in self.periodic_daterange:
period = self.get_period(end_date)
self.columns.append({
@ -129,7 +139,7 @@ class Analytics(object):
value_field = 'qty'
self.entries = frappe.db.sql("""
select i.item_code as entity, i.item_name as entity_name, i.{value_field} as value_field, s.{date_field}
select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field}
from `tab{doctype} Item` i , `tab{doctype}` s
where s.name = i.parent and i.docstatus = 1 and s.company = %s
and s.{date_field} between %s and %s
@ -198,6 +208,10 @@ class Analytics(object):
total += amount
row["total"] = total
if self.filters.tree_type == "Item":
row["stock_uom"] = period_data.get("stock_uom")
self.data.append(row)
def get_rows_by_group(self):
@ -232,6 +246,9 @@ class Analytics(object):
self.entity_periodic_data.setdefault(d.entity, frappe._dict()).setdefault(period, 0.0)
self.entity_periodic_data[d.entity][period] += flt(d.value_field)
if self.filters.tree_type == "Item":
self.entity_periodic_data[d.entity]['stock_uom'] = d.stock_uom
def get_period(self, posting_date):
if self.filters.range == 'Weekly':
period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)

View File

@ -1,553 +1,194 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"_comments": "[]",
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:customer_group_name",
"beta": 0,
"creation": "2013-01-10 16:34:23",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"field_order": [
"customer_group_name",
"parent_customer_group",
"is_group",
"cb0",
"default_price_list",
"payment_terms",
"lft",
"rgt",
"old_parent",
"default_receivable_account",
"accounts",
"credit_limit_section",
"credit_limits"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "customer_group_name",
"fieldtype": "Data",
"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": "Customer Group Name",
"length": 0,
"no_copy": 1,
"oldfieldname": "customer_group_name",
"oldfieldtype": "Data",
"permlevel": 0,
"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": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "parent_customer_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Parent Customer Group",
"length": 0,
"no_copy": 0,
"oldfieldname": "parent_customer_group",
"oldfieldtype": "Link",
"options": "Customer Group",
"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
"options": "Customer Group"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0",
"description": "Only leaf nodes are allowed in transaction",
"fieldname": "is_group",
"fieldtype": "Check",
"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": "Is Group",
"length": 0,
"no_copy": 0,
"oldfieldname": "is_group",
"oldfieldtype": "Select",
"options": "",
"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
"oldfieldtype": "Select"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cb0",
"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,
"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
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_price_list",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"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
"options": "Price List"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "payment_terms",
"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": "Default Payment Terms Template",
"length": 0,
"no_copy": 0,
"options": "Payment Terms Template",
"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
"options": "Payment Terms Template"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "credit_limit",
"fieldtype": "Currency",
"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": "Credit Limit",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"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": "lft",
"fieldtype": "Int",
"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": "lft",
"length": 0,
"no_copy": 1,
"oldfieldname": "lft",
"oldfieldtype": "Int",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rgt",
"fieldtype": "Int",
"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": "rgt",
"length": 0,
"no_copy": 1,
"oldfieldname": "rgt",
"oldfieldtype": "Int",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "old_parent",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "old_parent",
"length": 0,
"no_copy": 1,
"oldfieldname": "old_parent",
"oldfieldtype": "Data",
"options": "Customer Group",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"report_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_receivable_account",
"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,
"label": "Default Receivable Account",
"length": 0,
"no_copy": 0,
"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
"label": "Default Receivable Account"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"description": "Mention if non-standard receivable account applicable",
"fieldname": "accounts",
"fieldtype": "Table",
"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": "Accounts",
"length": 0,
"no_copy": 0,
"options": "Party Account",
"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
"options": "Party Account"
},
{
"fieldname": "credit_limit_section",
"fieldtype": "Section Break",
"label": "Credit Limits"
},
{
"fieldname": "credit_limits",
"fieldtype": "Table",
"label": "Credit Limit",
"options": "Customer Credit Limit"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-sitemap",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-29 06:26:05.935871",
"modified": "2019-09-06 12:40:14.954697",
"modified_by": "Administrator",
"module": "Setup",
"name": "Customer Group",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"role": "Sales Manager"
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"role": "Sales User"
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Master Manager",
"set_user_permissions": 1,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 0,
"read": 1,
"report": 0,
"role": "Sales Master Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 0,
"read": 1,
"report": 0,
"role": "Sales User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"role": "Sales User"
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 0,
"read": 1,
"report": 0,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"role": "Sales Manager"
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "parent_customer_group",
"show_name_in_global_search": 1,
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
"sort_order": "DESC"
}

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
import copy
from frappe import _
from frappe.utils import nowdate, cint, cstr
from frappe.utils.nestedset import NestedSet
from frappe.website.website_generator import WebsiteGenerator
@ -28,7 +29,8 @@ class ItemGroup(NestedSet, WebsiteGenerator):
super(ItemGroup, self).validate()
if not self.parent_item_group and not frappe.flags.in_test:
self.parent_item_group = 'All Item Groups'
if frappe.db.exists("Item Group", _('All Item Groups'), cache=True):
self.parent_item_group = _('All Item Groups')
self.make_route()

View File

@ -336,19 +336,20 @@ def set_price_list_and_rate(quotation, cart_settings):
def _set_price_list(quotation, cart_settings):
"""Set price list based on customer or shopping cart default"""
if quotation.selling_price_list:
return
from erpnext.accounts.party import get_default_price_list
# check if customer price list exists
selling_price_list = None
if quotation.party_name:
from erpnext.accounts.party import get_default_price_list
selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name))
selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list')
# else check for territory based price list
if not selling_price_list:
selling_price_list = cart_settings.price_list
if not selling_price_list and quotation.party_name:
selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name))
quotation.selling_price_list = selling_price_list
def set_taxes(quotation, cart_settings):

View File

@ -234,8 +234,10 @@ class DeliveryNote(SellingController):
extra_amount = 0
validate_against_credit_limit = False
bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer", self.customer,
"bypass_credit_limit_check_at_sales_order"))
bypass_credit_limit_check_at_sales_order = cint(frappe.db.get_value("Customer Credit Limit",
filters={'parent': self.customer, 'parenttype': 'Customer', 'company': self.company},
fieldname="bypass_credit_limit_check"))
if bypass_credit_limit_check_at_sales_order:
validate_against_credit_limit = True
extra_amount = self.base_grand_total

View File

@ -238,7 +238,7 @@ class DeliveryTrip(Document):
try:
directions = maps_client.directions(**directions_data)
except Exception as e:
frappe.throw(_(e.message))
frappe.throw(_(e))
return directions[0] if directions else False

View File

@ -49,7 +49,7 @@ class PackingSlip(Document):
frappe.msgprint(_("Please specify a valid 'From Case No.'"), raise_exception=1)
elif not self.to_case_no:
self.to_case_no = self.from_case_no
elif self.from_case_no > self.to_case_no:
elif cint(self.from_case_no) > cint(self.to_case_no):
frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"),
raise_exception=1)

View File

@ -639,7 +639,7 @@ def validate_price_list(args):
if not frappe.db.get_value("Price List",
{"name": args.price_list, args.transaction_type: 1, "enabled": 1}):
throw(_("Price List {0} is disabled or does not exist").format(args.price_list))
elif not args.get("supplier"):
elif args.get("customer"):
throw(_("Price List not selected"))
def validate_conversion_rate(args, meta):

View File

@ -181,10 +181,7 @@ class update_entries_after(object):
# rounding as per precision
self.stock_value = flt(self.stock_value, self.precision)
if self.prev_stock_value < 0 and self.stock_value >= 0 and sle.voucher_type != 'Stock Reconciliation':
stock_value_difference = sle.actual_qty * self.valuation_rate
else:
stock_value_difference = self.stock_value - self.prev_stock_value
stock_value_difference = self.stock_value - self.prev_stock_value
self.prev_stock_value = self.stock_value

View File

@ -15,7 +15,7 @@ def get_stock_value_from_bin(warehouse=None, item_code=None):
values = {}
conditions = ""
if warehouse:
conditions += """ and warehouse in (
conditions += """ and `tabBin`.warehouse in (
select w2.name from `tabWarehouse` w1
join `tabWarehouse` w2 on
w1.name = %(warehouse)s
@ -25,11 +25,12 @@ def get_stock_value_from_bin(warehouse=None, item_code=None):
values['warehouse'] = warehouse
if item_code:
conditions += " and item_code = %(item_code)s"
conditions += " and `tabBin`.item_code = %(item_code)s"
values['item_code'] = item_code
query = "select sum(stock_value) from `tabBin` where 1 = 1 %s" % conditions
query = """select sum(stock_value) from `tabBin`, `tabItem` where 1 = 1
and `tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 %s""" % conditions
stock_value = frappe.db.sql(query, values)

View File

@ -1,3 +1,3 @@
{% set domains = frappe.get_doc("Domain Settings").active_domains %}
<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software</a>
<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }}</a>