mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-08 04:01:46 +00:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292f46fec0 | ||
|
|
78a0be666f | ||
|
|
ce4f520908 | ||
|
|
29160441bb | ||
|
|
1b18bba04a | ||
|
|
f8c26bb778 | ||
|
|
ccd0617e19 | ||
|
|
bf10d0f98b | ||
|
|
dbb67fb4fd | ||
|
|
404f39d373 | ||
|
|
bbee9b6cc4 | ||
|
|
69b0535e10 | ||
|
|
7f63c1ad7c | ||
|
|
ebe1ebead2 | ||
|
|
f2b3307136 | ||
|
|
9d215c2d9b | ||
|
|
3cf0d51e65 | ||
|
|
9fc5ddc26e | ||
|
|
0ff31f0ebf | ||
|
|
40349f4b00 | ||
|
|
b36517158b | ||
|
|
3b9720f0be | ||
|
|
cf26b2ca1c | ||
|
|
02a40e9d7b | ||
|
|
ea199f9cd9 | ||
|
|
161f35a687 | ||
|
|
fcb984b294 | ||
|
|
723fe8fb1b | ||
|
|
47c0c8ba49 | ||
|
|
3e0a937cb8 | ||
|
|
bd63f0056a | ||
|
|
55daa9cd24 | ||
|
|
764b9bfeba | ||
|
|
62b985d405 | ||
|
|
ed393d1025 | ||
|
|
ed376cacc8 | ||
|
|
7461806b1c | ||
|
|
bb7c5ac0f8 | ||
|
|
0272fc05bf | ||
|
|
4f6b68fef2 | ||
|
|
eae2ddac69 | ||
|
|
99be9d17d5 | ||
|
|
5df63e36d4 | ||
|
|
bd99606a14 | ||
|
|
b0a46f397a | ||
|
|
53c9b63c0b | ||
|
|
9b2078feab | ||
|
|
30304e68ff | ||
|
|
6bc500bffa | ||
|
|
8be895091e | ||
|
|
f5f8a1f288 | ||
|
|
c925a38f79 | ||
|
|
c23230faea | ||
|
|
6855b87f76 | ||
|
|
07d030208f | ||
|
|
47f6e32920 | ||
|
|
7bd1453f8f | ||
|
|
bf416cfbf8 | ||
|
|
67918fff32 | ||
|
|
132dac9712 | ||
|
|
57914f140e | ||
|
|
ede0af97cd | ||
|
|
ad4ede0e23 | ||
|
|
cfcf9b3832 | ||
|
|
d2065ce3bf | ||
|
|
31edddace6 | ||
|
|
8f29159fb7 | ||
|
|
d9f1b539dc | ||
|
|
60bd383a25 | ||
|
|
c0ec3c3f7b | ||
|
|
60aa1170d5 | ||
|
|
9630aa3d7f | ||
|
|
aa6576c0ea | ||
|
|
fa5ecb066e | ||
|
|
c36524ec54 | ||
|
|
cba3570cf6 | ||
|
|
5b7503c074 | ||
|
|
8441dd9184 | ||
|
|
78869f1e77 | ||
|
|
ff8f1bc88e | ||
|
|
91b0dce4d8 | ||
|
|
59f7b8c4a1 | ||
|
|
2476a8aab9 | ||
|
|
46ffbb74ff | ||
|
|
492aff2a8b | ||
|
|
c3fbc04aab | ||
|
|
5b866db6bc | ||
|
|
9fbb8192e1 | ||
|
|
ea1408f2e0 | ||
|
|
f2a941b1a4 | ||
|
|
ca1cfd91ea | ||
|
|
c3d43e672b | ||
|
|
e141fdcfcc | ||
|
|
7cb8f890bc | ||
|
|
d6aadef395 | ||
|
|
11c899bc1f | ||
|
|
00efeb26f4 | ||
|
|
1129ac711e | ||
|
|
2ea593bdd8 | ||
|
|
02fd6f56d6 | ||
|
|
098b444f6f | ||
|
|
e72bde5159 | ||
|
|
c10e106f8f | ||
|
|
2c111b7b14 | ||
|
|
f92fcdbac4 | ||
|
|
1ff9e0432a | ||
|
|
c0f149a3be | ||
|
|
2ae6cfda5f | ||
|
|
22d9f0a4aa | ||
|
|
aa7cddef5c | ||
|
|
3d2b3d37d4 | ||
|
|
821222653b | ||
|
|
adddb15380 | ||
|
|
5fd79f7f77 | ||
|
|
2505c74d64 | ||
|
|
d5f9ebd008 | ||
|
|
d021e45301 | ||
|
|
e9ca5e81d2 | ||
|
|
a7f5589564 | ||
|
|
a8df73c263 | ||
|
|
40a5a3063c | ||
|
|
d17c24f969 | ||
|
|
0289ad285f | ||
|
|
562227db8f | ||
|
|
9c1cac80fe | ||
|
|
03016e5000 | ||
|
|
9240eaa0a6 | ||
|
|
618940b5c9 | ||
|
|
ccf2b7b08b | ||
|
|
1e989b35ad | ||
|
|
cae2723920 | ||
|
|
badc855400 | ||
|
|
a4502c4f6b | ||
|
|
1f02c62339 | ||
|
|
ef770b593a | ||
|
|
a9c90c96a9 | ||
|
|
c5d222253a | ||
|
|
94769d81a8 | ||
|
|
edd63a178f | ||
|
|
ace68cd283 | ||
|
|
d3bd151c30 | ||
|
|
0794816527 | ||
|
|
7041a45294 | ||
|
|
a6746407e5 |
@@ -11,6 +11,7 @@ install:
|
||||
- pip install flake8==3.3.0
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
- sudo rm /etc/apt/sources.list.d/docker.list
|
||||
- sudo apt-get install hhvm && rm -rf /home/travis/.kiex/
|
||||
- sudo apt-get purge -y mysql-common mysql-server mysql-client
|
||||
- nvm install v7.10.0
|
||||
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py
|
||||
@@ -32,6 +33,7 @@ before_script:
|
||||
- bench reinstall --yes
|
||||
- bench build
|
||||
- bench scheduler disable
|
||||
- sed -i 's/9000/9001/g' sites/common_site_config.json
|
||||
- bench start &
|
||||
- sleep 10
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '10.1.16'
|
||||
__version__ = '10.1.33'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -71,7 +71,7 @@ frappe.treeview_settings["Account"] = {
|
||||
|
||||
// financial statements
|
||||
for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
|
||||
'Profit and Loss', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||
'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||
treeview.page.add_inner_button(__(report), function() {
|
||||
frappe.set_route('query-report', report, {company: get_company()});
|
||||
}, __('Financial Statements'));
|
||||
|
||||
@@ -165,7 +165,7 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name] = {}
|
||||
|
||||
# assign account_type and root_type
|
||||
if child.account_type:
|
||||
if child.account_number:
|
||||
tree[child.account_name]["account_number"] = child.account_number
|
||||
if child.account_type:
|
||||
tree[child.account_name]["account_type"] = child.account_type
|
||||
@@ -175,4 +175,4 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name]["root_type"] = child.root_type
|
||||
|
||||
# call recursively to build a subtree for current account
|
||||
build_account_tree(tree[child.account_name], child, all_accounts)
|
||||
build_account_tree(tree[child.account_name], child, all_accounts)
|
||||
|
||||
@@ -27,22 +27,32 @@ def create_or_update_cheque_print_format(template_name):
|
||||
doc = frappe.get_doc("Cheque Print Template", template_name)
|
||||
|
||||
cheque_print.html = """
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 0px;
|
||||
}
|
||||
@media screen {
|
||||
.print-format {
|
||||
padding: 0in;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
||||
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
||||
<span style="top: {{ %(acc_pay_dist_from_top_edge)s }}cm; left: {{ %(acc_pay_dist_from_left_edge)s }}cm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; position: absolute;">
|
||||
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||
%(message_to_show)s
|
||||
</span>
|
||||
<span style="top:%(date_dist_from_top_edge)s cm; left:%(date_dist_from_left_edge)scm;
|
||||
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
||||
</span>
|
||||
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{ doc.account_no or '' }}
|
||||
</span>
|
||||
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.party_name}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm;
|
||||
@@ -51,11 +61,11 @@ def create_or_update_cheque_print_format(template_name):
|
||||
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 4cm;">
|
||||
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}
|
||||
</span>
|
||||
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.company}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -48,5 +48,10 @@ frappe.treeview_settings["Cost Center"] = {
|
||||
}, __('Budget'));
|
||||
|
||||
},
|
||||
onrender: function(node) {
|
||||
if(node.is_root){
|
||||
node.hide_add = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class JournalEntry(AccountsController):
|
||||
d.reference_type = ''
|
||||
d.reference_name = ''
|
||||
d.db_update()
|
||||
|
||||
|
||||
def unlink_asset_reference(self):
|
||||
for d in self.get("accounts"):
|
||||
if d.reference_type=="Asset" and d.reference_name:
|
||||
@@ -125,7 +125,7 @@ class JournalEntry(AccountsController):
|
||||
if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(d.party_type == 'Supplier' and flt(d.debit) > 0):
|
||||
if d.is_advance=="No":
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account), alert=1)
|
||||
elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ class OpeningInvoiceCreationTool(Document):
|
||||
income_expense_account_field = "expense_account"
|
||||
|
||||
item = get_item_dict()
|
||||
return frappe._dict({
|
||||
|
||||
args = frappe._dict({
|
||||
"items": [item],
|
||||
"is_opening": "Yes",
|
||||
"set_posting_time": 1,
|
||||
@@ -150,6 +151,11 @@ class OpeningInvoiceCreationTool(Document):
|
||||
"currency": frappe.db.get_value("Company", self.company, "default_currency")
|
||||
})
|
||||
|
||||
if self.invoice_type == "Sales":
|
||||
args["is_pos"] = 0
|
||||
|
||||
return args
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_temporary_opening_account(company=None):
|
||||
if not company:
|
||||
|
||||
@@ -768,6 +768,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
party_account = doc.receivable_account
|
||||
elif dt == "Employee Advance":
|
||||
party_account = doc.advance_account
|
||||
elif dt == "Expense Claim":
|
||||
party_account = doc.payable_account
|
||||
else:
|
||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||
|
||||
|
||||
@@ -319,6 +319,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
},
|
||||
|
||||
pos_profile: function() {
|
||||
this.frm.doc.taxes = []
|
||||
this.set_pos_data();
|
||||
},
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ class SalesInvoice(SellingController):
|
||||
for item in self.get("items"):
|
||||
if item.get('item_code'):
|
||||
for fname, val in get_pos_profile_item_details(pos,
|
||||
frappe._dict(item.as_dict()), pos).items():
|
||||
frappe._dict(item.as_dict()), pos, True).items():
|
||||
|
||||
if (not for_validate) or (for_validate and not item.get(fname)):
|
||||
item.set(fname, val)
|
||||
|
||||
@@ -39,18 +39,20 @@ frappe.ui.form.on("Tax Rule", "customer", function(frm) {
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "supplier", function(frm) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.supplier,
|
||||
"party_type": "supplier"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
if(frm.doc.supplier) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.supplier,
|
||||
"party_type": "supplier"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,17 +2,30 @@
|
||||
<style>
|
||||
@media screen {
|
||||
.print-format {
|
||||
padding: 8mm;
|
||||
margin:4mm;
|
||||
font-size:10px;
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
font-family: Tahoma, sans-serif;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% } %}
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
font-family: Tahoma, sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
|
||||
<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 %}
|
||||
@@ -32,7 +45,7 @@
|
||||
var range4 = report.columns[14].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed table-sm small">
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
|
||||
<colgroup>
|
||||
<col style="width: 30mm;">
|
||||
@@ -91,8 +104,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 15%">{%= __("Date") %}</th>
|
||||
<th style="width: 20%">{%= __("Reference") %}</th>
|
||||
<th style="width: 18%">{%= __("Date") %}</th>
|
||||
<th style="width: 17%">{%= __("Reference") %}</th>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
@@ -126,9 +139,14 @@
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
|
||||
<td>{%= dateutil.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td>{%= data[i]["voucher_type"] %}
|
||||
<br>{%= data[i]["voucher_no"] %}</td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
<br>
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
|
||||
@@ -220,4 +238,4 @@
|
||||
{% } %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-right text-muted">{{ __("Printed On") }}{%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>
|
||||
<p class="text-right text-muted">{{ __("Printed On ") }}{%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>
|
||||
@@ -17,8 +17,9 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"options": "Customer",
|
||||
on_change: () => {
|
||||
var customer = frappe.query_report_filters_by_name.customer.get_value();
|
||||
frappe.db.get_value('Customer', customer, "tax_id", function(value) {
|
||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name"], function(value) {
|
||||
frappe.query_report_filters_by_name.tax_id.set_value(value["tax_id"]);
|
||||
frappe.query_report_filters_by_name.customer_name.set_value(value["customer_name"]);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -81,6 +82,12 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"label": __("Tax Id"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"customer_name",
|
||||
"label": __("Customer Name"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class ReceivablePayableReport(object):
|
||||
return_entries = self.get_return_entries(args.get("party_type"))
|
||||
|
||||
data = []
|
||||
pdc_details = get_pdc_details(args.get("party_type"))
|
||||
pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
|
||||
|
||||
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
|
||||
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
|
||||
@@ -182,6 +182,7 @@ class ReceivablePayableReport(object):
|
||||
row.append(company_currency)
|
||||
|
||||
pdc = pdc_details.get((gle.voucher_no, gle.party), {})
|
||||
|
||||
remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount"))
|
||||
row += [pdc.get("pdc_date"), pdc.get("pdc_ref"),
|
||||
flt(pdc.get("pdc_amount")), remaining_balance]
|
||||
@@ -398,7 +399,7 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat
|
||||
|
||||
return [age] + outstanding_range
|
||||
|
||||
def get_pdc_details(party_type):
|
||||
def get_pdc_details(party_type, report_date):
|
||||
pdc_details = frappe._dict()
|
||||
|
||||
for pdc in frappe.db.sql("""
|
||||
@@ -411,13 +412,14 @@ def get_pdc_details(party_type):
|
||||
on
|
||||
(pref.parent = pent.name)
|
||||
where
|
||||
pent.docstatus < 2 and pent.reference_date >= pent.posting_date
|
||||
pent.docstatus < 2 and pent.reference_date >= %s
|
||||
and pent.party_type = %s
|
||||
group by pent.party, pref.reference_name""", party_type, as_dict=1):
|
||||
group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1):
|
||||
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
|
||||
|
||||
if scrub(party_type):
|
||||
amount_field = "jea.debit_in_account_currency + jea.credit_in_account_currency"
|
||||
amount_field = ("jea.debit_in_account_currency"
|
||||
if party_type == 'Supplier' else "jea.credit_in_account_currency")
|
||||
else:
|
||||
amount_field = "jea.debit + jea.credit"
|
||||
|
||||
@@ -431,9 +433,9 @@ def get_pdc_details(party_type):
|
||||
on
|
||||
(jea.parent = je.name)
|
||||
where
|
||||
je.docstatus < 2 and je.cheque_date >= je.posting_date
|
||||
je.docstatus < 2 and je.cheque_date >= %s
|
||||
and jea.party_type = %s
|
||||
group by jea.party, jea.reference_name""".format(amount_field), party_type, as_dict=1):
|
||||
group by jea.party, jea.reference_name""".format(amount_field), (report_date, party_type), as_dict=1):
|
||||
if (pdc.invoice_no, pdc.party) in pdc_details:
|
||||
pdc_details[(pdc.invoice_no, pdc.party)]["pdc_amount"] += pdc.pdc_amount
|
||||
else:
|
||||
|
||||
@@ -232,7 +232,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
tax_master_doctype = self.meta.get_field("taxes_and_charges").options
|
||||
|
||||
if self.is_new() and not self.get("taxes"):
|
||||
if (self.is_new() or self.is_pos_profile_changed()) and not self.get("taxes"):
|
||||
if self.company and not self.get("taxes_and_charges"):
|
||||
# get the default tax master
|
||||
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype,
|
||||
@@ -240,6 +240,11 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.append_taxes_from_master(tax_master_doctype)
|
||||
|
||||
def is_pos_profile_changed(self):
|
||||
if (self.doctype == 'Sales Invoice' and self.is_pos and
|
||||
self.pos_profile != frappe.db.get_value('Sales Invoice', self.name, 'pos_profile')):
|
||||
return True
|
||||
|
||||
def append_taxes_from_master(self, tax_master_doctype=None):
|
||||
if self.get("taxes_and_charges"):
|
||||
if not tax_master_doctype:
|
||||
@@ -666,7 +671,9 @@ class AccountsController(TransactionBase):
|
||||
self.remove(item)
|
||||
|
||||
def set_payment_schedule(self):
|
||||
if self.doctype == 'Sales Invoice' and self.is_pos: return
|
||||
if self.doctype == 'Sales Invoice' and self.is_pos:
|
||||
self.payment_terms_template = ''
|
||||
return
|
||||
|
||||
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
|
||||
date = self.get("due_date")
|
||||
|
||||
@@ -152,6 +152,11 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
||||
conditions = []
|
||||
|
||||
description_cond = ''
|
||||
if frappe.db.count('Item', cache=True) < 50000:
|
||||
# scan description only if items are less than 50000
|
||||
description_cond = 'or tabItem.description LIKE %(txt)s'
|
||||
|
||||
return frappe.db.sql("""select tabItem.name, tabItem.item_group,
|
||||
if(length(tabItem.item_name) > 40,
|
||||
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
|
||||
@@ -166,7 +171,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
or tabItem.item_group LIKE %(txt)s
|
||||
or tabItem.item_name LIKE %(txt)s
|
||||
or tabItem.barcode LIKE %(txt)s
|
||||
or tabItem.description LIKE %(txt)s)
|
||||
{description_cond})
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
@@ -176,7 +181,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
key=searchfield,
|
||||
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||
mcond=get_match_cond(doctype).replace('%', '%%')),
|
||||
mcond=get_match_cond(doctype).replace('%', '%%'),
|
||||
description_cond = description_cond),
|
||||
{
|
||||
"today": nowdate(),
|
||||
"txt": "%%%s%%" % txt,
|
||||
|
||||
@@ -83,15 +83,15 @@ def validate_returned_items(doc):
|
||||
else:
|
||||
ref = valid_items.get(d.item_code, frappe._dict())
|
||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
||||
|
||||
|
||||
if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
|
||||
frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}")
|
||||
.format(d.idx, doc.doctype, doc.return_against))
|
||||
|
||||
|
||||
elif ref.batch_no and d.batch_no not in ref.batch_no:
|
||||
frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
|
||||
.format(d.idx, doc.doctype, doc.return_against))
|
||||
|
||||
|
||||
elif ref.serial_no:
|
||||
if not d.serial_no:
|
||||
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
|
||||
@@ -120,25 +120,30 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||
|
||||
for column in fields:
|
||||
returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
|
||||
reference_qty = (ref.get(column) if column == 'stock_qty'
|
||||
else ref.get(column) * ref.get("conversion_factor", 1.0))
|
||||
|
||||
if column == 'stock_qty':
|
||||
reference_qty = ref.get(column)
|
||||
current_stock_qty = args.get(column)
|
||||
else:
|
||||
reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0)
|
||||
current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
|
||||
|
||||
max_returnable_qty = flt(reference_qty) - returned_qty
|
||||
label = column.replace('_', ' ').title()
|
||||
|
||||
if reference_qty:
|
||||
if reference_qty:
|
||||
if flt(args.get(column)) > 0:
|
||||
frappe.throw(_("{0} must be negative in return document").format(label))
|
||||
elif returned_qty >= reference_qty and args.get(column):
|
||||
frappe.throw(_("Item {0} has already been returned")
|
||||
.format(args.item_code), StockOverReturnError)
|
||||
elif (abs(args.get(column)) * args.get("conversion_factor", 1.0)) > max_returnable_qty:
|
||||
elif abs(current_stock_qty) > max_returnable_qty:
|
||||
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
|
||||
.format(args.idx, reference_qty, args.item_code), StockOverReturnError)
|
||||
|
||||
def get_ref_item_dict(valid_items, ref_item_row):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
|
||||
valid_items.setdefault(ref_item_row.item_code, frappe._dict({
|
||||
"qty": 0,
|
||||
"rate": 0,
|
||||
@@ -160,10 +165,10 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
||||
|
||||
if ref_item_row.get("serial_no"):
|
||||
item_dict["serial_no"] += get_serial_nos(ref_item_row.serial_no)
|
||||
|
||||
|
||||
if ref_item_row.get("batch_no"):
|
||||
item_dict["batch_no"].append(ref_item_row.batch_no)
|
||||
|
||||
|
||||
return valid_items
|
||||
|
||||
def get_already_returned_items(doc):
|
||||
|
||||
@@ -29,6 +29,7 @@ class calculate_taxes_and_totals(object):
|
||||
self.set_item_wise_tax_breakup()
|
||||
|
||||
def _calculate(self):
|
||||
self.validate_conversion_rate()
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
@@ -557,7 +558,8 @@ def get_itemised_tax_breakup_html(doc):
|
||||
itemised_tax=itemised_tax,
|
||||
itemised_taxable_amount=itemised_taxable_amount,
|
||||
tax_accounts=tax_accounts,
|
||||
company_currency=erpnext.get_company_currency(doc.company)
|
||||
conversion_rate=doc.conversion_rate,
|
||||
currency=doc.currency
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ frappe.ui.form.on("Opportunity", {
|
||||
frm.trigger('set_contact_link');
|
||||
},
|
||||
|
||||
with_items: function(frm) {
|
||||
frm.trigger('toggle_mandatory');
|
||||
},
|
||||
|
||||
customer_address: function(frm, cdt, cdn) {
|
||||
erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false);
|
||||
},
|
||||
@@ -35,6 +39,7 @@ frappe.ui.form.on("Opportunity", {
|
||||
var doc = frm.doc;
|
||||
frm.events.enquiry_from(frm);
|
||||
frm.trigger('set_contact_link');
|
||||
frm.trigger('toggle_mandatory');
|
||||
erpnext.toggle_naming_series();
|
||||
|
||||
if(!doc.__islocal && doc.status!=="Lost") {
|
||||
@@ -84,6 +89,10 @@ frappe.ui.form.on("Opportunity", {
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_supplier_quotation",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
toggle_mandatory: function(frm) {
|
||||
frm.toggle_reqd("items", frm.doc.with_items ? 1:0);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -34,14 +34,14 @@ def work():
|
||||
payroll_entry.salary_slip_based_on_timesheet = 0
|
||||
payroll_entry.create_salary_slips()
|
||||
payroll_entry.submit_salary_slips()
|
||||
payroll_entry.make_accural_jv_entry()
|
||||
payroll_entry.make_accrual_jv_entry()
|
||||
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
|
||||
# reference_number=random_string(10))
|
||||
|
||||
payroll_entry.salary_slip_based_on_timesheet = 1
|
||||
payroll_entry.create_salary_slips()
|
||||
payroll_entry.submit_salary_slips()
|
||||
payroll_entry.make_accural_jv_entry()
|
||||
payroll_entry.make_accrual_jv_entry()
|
||||
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
|
||||
# reference_number=random_string(10))
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ We can also send the feedback request to Customer/User without configuring the
|
||||
Feedback Trigger.
|
||||
|
||||
To request a feedback manually go to respective document e.g. Sales Order, Issue etc.
|
||||
and click on Ask a Feedback option in Menu.
|
||||
and click on Request Feedback option in Menu.
|
||||
|
||||
<img class="screenshot" alt="Setting Condition" src="/docs/assets/img/setup/feedback/manual-feedback-request-option.png">
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ In the document, you can select multiple Purchase Receipts and fetch all items f
|
||||
|
||||
<img class="screenshot" alt="Landed Cost Vouher" src="/docs/assets/img/stock/landed-cost.png">
|
||||
|
||||
### What happend on submission?
|
||||
### What happens on submission?
|
||||
|
||||
1. On submission of Landed Cost Voucher, the applicable landed cost charges are updated in Purchase Receipt Item table.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class ProgramEnrollmentTool(Document):
|
||||
elif self.get_students_from == "Program Enrollment":
|
||||
condition2 = 'and student_batch_name=%(student_batch)s' if self.student_batch else " "
|
||||
students = frappe.db.sql('''select student, student_name, student_batch_name from `tabProgram Enrollment`
|
||||
where program=%(program)s and academic_year=%(academic_year)s {0} {1}'''
|
||||
where program=%(program)s and academic_year=%(academic_year)s {0} {1} and docstatus != 2'''
|
||||
.format(condition, condition2), self.as_dict(), as_dict=1)
|
||||
|
||||
student_list = [d.student for d in students]
|
||||
|
||||
@@ -82,36 +82,39 @@ frappe.ui.form.on("Student Group", {
|
||||
max_roll_no = d.group_roll_number;
|
||||
}
|
||||
});
|
||||
frappe.call({
|
||||
method: "erpnext.education.doctype.student_group.student_group.get_students",
|
||||
args: {
|
||||
"academic_year": frm.doc.academic_year,
|
||||
"academic_term": frm.doc.academic_term,
|
||||
"group_based_on": frm.doc.group_based_on,
|
||||
"program": frm.doc.program,
|
||||
"batch" : frm.doc.batch,
|
||||
"course": frm.doc.course
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
$.each(r.message, function(i, d) {
|
||||
if(!in_list(student_list, d.student)) {
|
||||
var s = frm.add_child("students");
|
||||
s.student = d.student;
|
||||
s.student_name = d.student_name;
|
||||
if (d.active === 0) {
|
||||
s.active = 0;
|
||||
|
||||
if(frm.doc.academic_year) {
|
||||
frappe.call({
|
||||
method: "erpnext.education.doctype.student_group.student_group.get_students",
|
||||
args: {
|
||||
"academic_year": frm.doc.academic_year,
|
||||
"academic_term": frm.doc.academic_term,
|
||||
"group_based_on": frm.doc.group_based_on,
|
||||
"program": frm.doc.program,
|
||||
"batch" : frm.doc.batch,
|
||||
"course": frm.doc.course
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
$.each(r.message, function(i, d) {
|
||||
if(!in_list(student_list, d.student)) {
|
||||
var s = frm.add_child("students");
|
||||
s.student = d.student;
|
||||
s.student_name = d.student_name;
|
||||
if (d.active === 0) {
|
||||
s.active = 0;
|
||||
}
|
||||
s.group_roll_number = ++max_roll_no;
|
||||
}
|
||||
s.group_roll_number = ++max_roll_no;
|
||||
}
|
||||
});
|
||||
refresh_field("students");
|
||||
frm.save();
|
||||
} else {
|
||||
frappe.msgprint(__("Student Group is already updated."))
|
||||
});
|
||||
refresh_field("students");
|
||||
frm.save();
|
||||
} else {
|
||||
frappe.msgprint(__("Student Group is already updated."))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
frappe.msgprint(__("Select students manually for the Activity based Group"));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ frappe.ui.form.on('Student Report Generation Tool', {
|
||||
frm.page.clear_indicator();
|
||||
frm.page.set_primary_action(__('Print Report Card'), () => {
|
||||
let url = "/api/method/erpnext.education.doctype.student_report_generation_tool.student_report_generation_tool.preview_report_card";
|
||||
open_url_post(url, frm.doc, true);
|
||||
open_url_post(url, {"doc": frm.doc}, true);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe, json
|
||||
from frappe.model.document import Document
|
||||
from erpnext.education.api import get_grade
|
||||
from frappe.utils.pdf import get_pdf
|
||||
@@ -16,8 +16,8 @@ class StudentReportGenerationTool(Document):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def preview_report_card(**kwargs):
|
||||
doc = frappe._dict(**kwargs)
|
||||
def preview_report_card(doc):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
doc.students = [doc.student]
|
||||
if not (doc.student_name and doc.student_batch):
|
||||
program_enrollment = frappe.get_all("Program Enrollment", fields=["student_batch_name", "student_name"],
|
||||
@@ -33,7 +33,7 @@ def preview_report_card(**kwargs):
|
||||
course_criteria = get_courses_criteria(courses)
|
||||
|
||||
# get the assessment group as per the user selection
|
||||
if int(doc.include_all_assessment):
|
||||
if doc.include_all_assessment:
|
||||
assessment_groups = get_child_assessment_groups(doc.assessment_group)
|
||||
else:
|
||||
assessment_groups = [doc.assessment_group]
|
||||
@@ -55,7 +55,7 @@ def preview_report_card(**kwargs):
|
||||
"assessment_groups": assessment_groups,
|
||||
"course_criteria": course_criteria,
|
||||
"letterhead": letterhead.content,
|
||||
"add_letterhead": int(doc.add_letterhead) if int(doc.add_letterhead) else 0
|
||||
"add_letterhead": doc.add_letterhead if doc.add_letterhead else 0
|
||||
})
|
||||
final_template = frappe.render_template(base_template_path, {"body": html, "title": "Report Card"})
|
||||
|
||||
|
||||
@@ -113,9 +113,11 @@ def insert_consultation_to_medical_record(doc):
|
||||
|
||||
def update_consultation_to_medical_record(consultation):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (consultation.name))
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
subject = set_subject_field(consultation)
|
||||
frappe.db.set_value("Patient Medical Record", medical_record_id[0][0], "subject", subject)
|
||||
else:
|
||||
insert_consultation_to_medical_record(consultation)
|
||||
|
||||
def delete_medical_record(consultation):
|
||||
frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (consultation.name))
|
||||
|
||||
@@ -244,7 +244,7 @@ def insert_lab_test_to_medical_record(doc):
|
||||
def delete_lab_test_from_medical_record(self):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(self.name))
|
||||
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
frappe.delete_doc("Patient Medical Record", medical_record_id[0][0])
|
||||
|
||||
def create_item_line(test_code, sales_invoice):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
frappe.views.calendar["Patient Appointment"] = {
|
||||
field_map: {
|
||||
"start": "start",
|
||||
"end": "end",
|
||||
"start": "appointment_date",
|
||||
"end": "appointment_datetime",
|
||||
"id": "name",
|
||||
"title": "patient",
|
||||
"allDay": "allDay"
|
||||
|
||||
@@ -27,7 +27,7 @@ def insert_vital_signs_to_medical_record(doc):
|
||||
|
||||
def delete_vital_signs_from_medical_record(doc):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(doc.name))
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
frappe.delete_doc("Patient Medical Record", medical_record_id[0][0])
|
||||
|
||||
def set_subject_field(doc):
|
||||
|
||||
@@ -204,7 +204,7 @@ doc_events = {
|
||||
'Address': {
|
||||
'validate': 'erpnext.regional.india.utils.validate_gstin_for_india'
|
||||
},
|
||||
('Sales Invoice', 'Purchase Invoice'): {
|
||||
('Sales Invoice', 'Purchase Invoice', 'Delivery Note'): {
|
||||
'validate': 'erpnext.regional.india.utils.set_place_of_supply'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,17 +20,19 @@ class Attendance(Document):
|
||||
set_employee_name(self)
|
||||
|
||||
def check_leave_record(self):
|
||||
leave_record = frappe.db.sql("""select leave_type, half_day from `tabLeave Application`
|
||||
leave_record = frappe.db.sql("""select leave_type, half_day, half_day_date from `tabLeave Application`
|
||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
||||
and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True)
|
||||
if leave_record:
|
||||
if leave_record[0].half_day:
|
||||
self.status = 'Half Day'
|
||||
frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date))
|
||||
else:
|
||||
self.status = 'On Leave'
|
||||
self.leave_type = leave_record[0].leave_type
|
||||
frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date))
|
||||
for d in leave_record:
|
||||
if d.half_day_date == getdate(self.attendance_date):
|
||||
self.status = 'Half Day'
|
||||
frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date))
|
||||
else:
|
||||
self.status = 'On Leave'
|
||||
self.leave_type = d.leave_type
|
||||
frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date))
|
||||
|
||||
if self.status == "On Leave" and not leave_record:
|
||||
frappe.throw(_("No leave record found for employee {0} for {1}").format(self.employee, self.attendance_date))
|
||||
|
||||
|
||||
@@ -98,9 +98,9 @@ cur_frm.cscript.refresh = function(doc) {
|
||||
if (cint(doc.total_amount_reimbursed) > 0 && frappe.model.can_read(entry_doctype)) {
|
||||
cur_frm.add_custom_button(__('Bank Entries'), function() {
|
||||
frappe.route_options = {
|
||||
entry_route_doctype: me.frm.doc.doctype,
|
||||
entry_route_name: me.frm.doc.name,
|
||||
company: me.frm.doc.company
|
||||
party_type: "Employee",
|
||||
party: doc.employee,
|
||||
company: doc.company
|
||||
};
|
||||
frappe.set_route("List", entry_doctype);
|
||||
}, __("View"));
|
||||
@@ -205,7 +205,7 @@ frappe.ui.form.on("Expense Claim", {
|
||||
make_payment_entry: function(frm) {
|
||||
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
||||
if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
|
||||
method = "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry"
|
||||
method = "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry";
|
||||
}
|
||||
return frappe.call({
|
||||
method: method,
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
frappe.views.calendar["Holiday List"] = {
|
||||
field_map: {
|
||||
"start": "holiday_date",
|
||||
"end": "holiday_date",
|
||||
"start": "from_date",
|
||||
"end": "to_date",
|
||||
"id": "name",
|
||||
"title": "description",
|
||||
"allDay": "allDay"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
@@ -11,6 +12,7 @@
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -21,6 +23,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee Settings",
|
||||
@@ -38,6 +41,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -50,6 +54,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Retirement Age",
|
||||
@@ -68,6 +73,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -80,6 +86,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee Records to be created by",
|
||||
@@ -98,6 +105,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -108,6 +116,7 @@
|
||||
"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,
|
||||
@@ -125,6 +134,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -136,6 +146,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Stop Birthday Reminders",
|
||||
@@ -153,6 +164,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -163,6 +175,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintain Billing Hours and Working Hours Same on Timesheet",
|
||||
@@ -181,6 +194,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -191,6 +205,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payroll Settings",
|
||||
@@ -208,6 +223,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -219,6 +235,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Include holidays in Total no. of Working Days",
|
||||
@@ -236,6 +253,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -248,6 +266,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Email Salary Slip to Employee",
|
||||
@@ -266,6 +285,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -276,6 +296,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Max working hours against Timesheet",
|
||||
@@ -292,21 +313,81 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "leave_settings",
|
||||
"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": "Leave Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "show_leaves_of_all_department_members_in_calendar",
|
||||
"fieldtype": "Check",
|
||||
"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": "Show Leaves Of All Department Members In Calendar",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-01-16 14:01:31.183485",
|
||||
"modified_by": "anastasiadis.st00@gmail.com",
|
||||
"modified": "2018-05-03 15:35:13.015466",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR Settings",
|
||||
"owner": "Administrator",
|
||||
@@ -321,7 +402,6 @@
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
@@ -336,6 +416,7 @@
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
|
||||
23
erpnext/hr/doctype/hr_settings/test_hr_settings.js
Normal file
23
erpnext/hr/doctype/hr_settings/test_hr_settings.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: HR Settings", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new HR Settings
|
||||
() => frappe.tests.make('HR Settings', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
10
erpnext/hr/doctype/hr_settings/test_hr_settings.py
Normal file
10
erpnext/hr/doctype/hr_settings/test_hr_settings.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestHRSettings(unittest.TestCase):
|
||||
pass
|
||||
@@ -95,6 +95,9 @@ frappe.ui.form.on("Leave Application", {
|
||||
if (!r.exc && r.message) {
|
||||
frm.set_value('leave_balance', r.message);
|
||||
}
|
||||
else {
|
||||
frm.set_value('leave_balance', "0");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \
|
||||
comma_or, get_fullname
|
||||
comma_or, get_fullname, nowdate
|
||||
from erpnext.hr.utils import set_employee_name
|
||||
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from erpnext.hr.doctype.employee_leave_approver.employee_leave_approver import get_approver_list
|
||||
|
||||
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
|
||||
|
||||
class LeaveDayBlockedError(frappe.ValidationError): pass
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
@@ -52,6 +52,7 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted"))
|
||||
|
||||
self.validate_back_dated_application()
|
||||
self.update_attendance()
|
||||
|
||||
# notify leave applier about approval
|
||||
self.notify_employee(self.status)
|
||||
@@ -100,6 +101,41 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
|
||||
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
|
||||
|
||||
def update_attendance(self):
|
||||
if self.status == "Approved":
|
||||
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\
|
||||
and (attendance_date between %s and %s) and docstatus < 2""",(self.employee, self.from_date, self.to_date), as_dict=1)
|
||||
|
||||
if attendance:
|
||||
for d in attendance:
|
||||
doc = frappe.get_doc("Attendance", d.name)
|
||||
if getdate(self.half_day_date) == doc.attendance_date:
|
||||
status = "Half Day"
|
||||
else:
|
||||
status = "On Leave"
|
||||
frappe.db.sql("""update `tabAttendance` set status = %s, leave_type = %s\
|
||||
where name = %s""",(status, self.leave_type, d.name))
|
||||
|
||||
elif self.from_date <= nowdate():
|
||||
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
|
||||
date = dt.strftime("%Y-%m-%d")
|
||||
if not date == self.half_day_date:
|
||||
doc = frappe.new_doc("Attendance")
|
||||
doc.employee = self.employee
|
||||
doc.attendance_date = date
|
||||
doc.company = self.company
|
||||
doc.status = "On Leave"
|
||||
doc.leave_type = self.leave_type
|
||||
doc.submit()
|
||||
else:
|
||||
doc = frappe.new_doc("Attendance")
|
||||
doc.employee = self.employee
|
||||
doc.attendance_date = date
|
||||
doc.company = self.company
|
||||
doc.status = "Half Day"
|
||||
doc.leave_type = self.leave_type
|
||||
doc.submit()
|
||||
|
||||
def validate_salary_processed_days(self):
|
||||
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
|
||||
return
|
||||
@@ -384,7 +420,7 @@ def get_leave_allocation_records(date, employee=None):
|
||||
|
||||
return allocated_leaves
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_holidays(employee, from_date, to_date):
|
||||
'''get holidays between two dates for the given employee'''
|
||||
holiday_list = get_holiday_list_for_employee(employee)
|
||||
|
||||
@@ -146,6 +146,7 @@ class PayrollEntry(Document):
|
||||
ss_list = self.get_sal_slip_list(ss_status=0)
|
||||
submitted_ss = []
|
||||
not_submitted_ss = []
|
||||
frappe.flags.via_payroll_entry = True
|
||||
for ss in ss_list:
|
||||
ss_obj = frappe.get_doc("Salary Slip",ss[0])
|
||||
ss_dict = {}
|
||||
@@ -159,17 +160,24 @@ class PayrollEntry(Document):
|
||||
else:
|
||||
try:
|
||||
ss_obj.submit()
|
||||
submitted_ss.append(ss_dict)
|
||||
submitted_ss.append(ss_obj)
|
||||
|
||||
except frappe.ValidationError:
|
||||
not_submitted_ss.append(ss_dict)
|
||||
if submitted_ss:
|
||||
jv_name = self.make_accural_jv_entry()
|
||||
jv_name = self.make_accrual_jv_entry()
|
||||
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
|
||||
.format(ss_obj.start_date, ss_obj.end_date))
|
||||
|
||||
self.email_salary_slip(submitted_ss)
|
||||
|
||||
return create_submit_log(submitted_ss, not_submitted_ss, jv_name)
|
||||
|
||||
def email_salary_slip(self, submitted_ss):
|
||||
if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"):
|
||||
for ss in submitted_ss:
|
||||
ss.email_salary_slip()
|
||||
|
||||
def get_loan_details(self):
|
||||
"""
|
||||
Get loan details from submitted salary slip based on selected criteria
|
||||
@@ -237,7 +245,7 @@ class PayrollEntry(Document):
|
||||
|
||||
return payroll_payable_account
|
||||
|
||||
def make_accural_jv_entry(self):
|
||||
def make_accrual_jv_entry(self):
|
||||
self.check_permission('write')
|
||||
earnings = self.get_salary_component_total(component_type = "earnings") or {}
|
||||
deductions = self.get_salary_component_total(component_type = "deductions") or {}
|
||||
@@ -249,7 +257,7 @@ class PayrollEntry(Document):
|
||||
if earnings or deductions:
|
||||
journal_entry = frappe.new_doc('Journal Entry')
|
||||
journal_entry.voucher_type = 'Journal Entry'
|
||||
journal_entry.user_remark = _('Accural Journal Entry for salaries from {0} to {1}')\
|
||||
journal_entry.user_remark = _('Accrual Journal Entry for salaries from {0} to {1}')\
|
||||
.format(self.start_date, self.end_date)
|
||||
journal_entry.company = self.company
|
||||
journal_entry.posting_date = self.posting_date
|
||||
|
||||
@@ -17,6 +17,5 @@ class SalaryComponent(Document):
|
||||
self.salary_component.split()]).upper()
|
||||
|
||||
self.salary_component_abbr = self.salary_component_abbr.strip()
|
||||
|
||||
self.salary_component_abbr = append_number_if_name_exists('Salary Component',
|
||||
self.salary_component_abbr, 'salary_component_abbr', separator='_')
|
||||
self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr,
|
||||
'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]})
|
||||
@@ -29,9 +29,10 @@ frappe.ui.form.on("Salary Slip", {
|
||||
})
|
||||
},
|
||||
|
||||
start_date: function(frm){
|
||||
start_date: function(frm, dt, dn){
|
||||
if(frm.doc.start_date){
|
||||
frm.trigger("set_end_date");
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -65,18 +66,20 @@ frappe.ui.form.on("Salary Slip", {
|
||||
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
||||
},
|
||||
|
||||
salary_slip_based_on_timesheet: function(frm) {
|
||||
salary_slip_based_on_timesheet: function(frm, dt, dn) {
|
||||
frm.trigger("toggle_fields");
|
||||
frm.set_value('start_date', '');
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
payroll_frequency: function(frm) {
|
||||
payroll_frequency: function(frm, dt, dn) {
|
||||
frm.trigger("toggle_fields");
|
||||
frm.set_value('end_date', '');
|
||||
frm.set_value('start_date', '');
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
employee: function(frm){
|
||||
frm.set_value('start_date', '');
|
||||
employee: function(frm, dt, dn) {
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
toggle_fields: function(frm) {
|
||||
@@ -109,7 +112,7 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
||||
|
||||
// Get leave details
|
||||
//---------------------------------------------------------------------
|
||||
cur_frm.cscript.start_date = function(doc, dt, dn){
|
||||
var get_emp_and_leave_details = function(doc, dt, dn) {
|
||||
if(!doc.start_date){
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
@@ -122,11 +125,9 @@ cur_frm.cscript.start_date = function(doc, dt, dn){
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date;
|
||||
|
||||
cur_frm.cscript.employee = function(doc,dt,dn){
|
||||
doc.salary_structure = ''
|
||||
cur_frm.cscript.start_date(doc, dt, dn)
|
||||
get_emp_and_leave_details(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
|
||||
|
||||
@@ -411,7 +411,7 @@ class SalarySlip(TransactionBase):
|
||||
else:
|
||||
self.set_status()
|
||||
self.update_status(self.name)
|
||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
|
||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
|
||||
self.email_salary_slip()
|
||||
|
||||
def on_cancel(self):
|
||||
|
||||
@@ -39,6 +39,16 @@ frappe.ui.form.on('Salary Structure', {
|
||||
}
|
||||
}
|
||||
});
|
||||
frm.set_query("payment_account", function () {
|
||||
var account_types = ["Bank", "Cash"];
|
||||
return {
|
||||
filters: {
|
||||
"account_type": ["in", account_types],
|
||||
"is_group": 0,
|
||||
"company": frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
@@ -151,6 +161,28 @@ frappe.ui.form.on('Salary Structure', {
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Salary Structure Employee', {
|
||||
from_date: function(frm, cdt, cdn) {
|
||||
validate_date(frm, cdt, cdn);
|
||||
},
|
||||
to_date: function(frm, cdt, cdn) {
|
||||
validate_date(frm, cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
var validate_date = function(frm, cdt, cdn) {
|
||||
var doc = locals[cdt][cdn];
|
||||
if(doc.to_date && doc.from_date) {
|
||||
var from_date = frappe.datetime.str_to_obj(doc.from_date);
|
||||
var to_date = frappe.datetime.str_to_obj(doc.to_date);
|
||||
|
||||
if(to_date < from_date) {
|
||||
frappe.model.set_value(cdt, cdn, "to_date", "");
|
||||
frappe.throw(__("From Date cannot be greater than To Date"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.amount = function(doc, cdt, cdn){
|
||||
calculate_totals(doc, cdt, cdn);
|
||||
|
||||
@@ -22,6 +22,10 @@ def execute(filters=None):
|
||||
holiday_map = get_holiday(holiday_list, filters["month"])
|
||||
|
||||
data = []
|
||||
leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
|
||||
leave_list = [d[0] for d in leave_types]
|
||||
columns.extend(leave_list)
|
||||
|
||||
for emp in sorted(att_map):
|
||||
emp_det = emp_map.get(emp)
|
||||
if not emp_det:
|
||||
@@ -49,10 +53,35 @@ def execute(filters=None):
|
||||
elif status == "Half Day":
|
||||
total_p += 0.5
|
||||
total_a += 0.5
|
||||
total_l += 0.5
|
||||
|
||||
row += [total_p, total_l, total_a]
|
||||
data.append(row)
|
||||
|
||||
if not filters.get("employee"):
|
||||
filters.update({"employee": emp})
|
||||
conditions += " and employee = %(employee)s"
|
||||
elif not filters.get("employee") == emp:
|
||||
filters.update({"employee": emp})
|
||||
|
||||
leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
|
||||
where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
|
||||
|
||||
leaves = {}
|
||||
for d in leave_details:
|
||||
if d.status == "Half Day":
|
||||
d.count = d.count * 0.5
|
||||
if d.leave_type in leaves:
|
||||
leaves[d.leave_type] += d.count
|
||||
else:
|
||||
leaves[d.leave_type] = d.count
|
||||
|
||||
for d in leave_list:
|
||||
if d in leaves:
|
||||
row.append(leaves[d])
|
||||
else:
|
||||
row.append("0.0")
|
||||
|
||||
data.append(row)
|
||||
return columns, data
|
||||
|
||||
def get_columns(filters):
|
||||
|
||||
@@ -602,6 +602,9 @@ def validate_bom_no(item, bom_no):
|
||||
for d in bom.items:
|
||||
if (d.item_code.lower() == item.lower()):
|
||||
rm_item_exists = True
|
||||
for d in bom.scrap_items:
|
||||
if (d.item_code.lower() == item.lower()):
|
||||
rm_item_exists = True
|
||||
if bom.item.lower() == item.lower() or \
|
||||
bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower():
|
||||
rm_item_exists = True
|
||||
|
||||
@@ -64,7 +64,7 @@ class ProductionOrder(Document):
|
||||
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
|
||||
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
|
||||
@@ -88,7 +88,7 @@ class ProductionOrder(Document):
|
||||
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"):
|
||||
@@ -524,7 +524,7 @@ def get_item_details(item, project = None):
|
||||
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))
|
||||
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))
|
||||
|
||||
@@ -642,5 +642,5 @@ def query_sales_order(production_item):
|
||||
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
|
||||
|
||||
@@ -12,6 +12,10 @@ frappe.query_reports["BOM Stock Report"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"reqd": 1
|
||||
}, {
|
||||
"fieldname": "show_exploded_view",
|
||||
"label": __("Show exploded view"),
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,52 +6,61 @@ import frappe
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
columns = get_columns()
|
||||
data = get_bom_stock(filters)
|
||||
return columns, data
|
||||
if not filters: filters = {}
|
||||
columns = get_columns()
|
||||
|
||||
data = get_bom_stock(filters)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
"""return columns"""
|
||||
columns = [
|
||||
_("Item") + ":Link/Item:150",
|
||||
_("Description") + "::500",
|
||||
_("Required Qty") + ":Float:100",
|
||||
_("In Stock Qty") + ":Float:100",
|
||||
_("Enough Parts to Build") + ":Float:200",
|
||||
]
|
||||
"""return columns"""
|
||||
columns = [
|
||||
_("Item") + ":Link/Item:150",
|
||||
_("Description") + "::500",
|
||||
_("Required Qty") + ":Float:100",
|
||||
_("In Stock Qty") + ":Float:100",
|
||||
_("Enough Parts to Build") + ":Float:200",
|
||||
]
|
||||
|
||||
return columns
|
||||
return columns
|
||||
|
||||
def get_bom_stock(filters):
|
||||
conditions = ""
|
||||
bom = filters.get("bom")
|
||||
conditions = ""
|
||||
bom = filters.get("bom")
|
||||
|
||||
if filters.get("warehouse"):
|
||||
warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
|
||||
if warehouse_details:
|
||||
conditions += " and exists (select name from `tabWarehouse` wh \
|
||||
where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
|
||||
warehouse_details.rgt)
|
||||
else:
|
||||
conditions += " and ledger.warehouse = '%s'" % frappe.db.escape(filters.get("warehouse"))
|
||||
table = "`tabBOM Item`"
|
||||
qty_field = "qty"
|
||||
|
||||
else:
|
||||
conditions += ""
|
||||
if filters.get("show_exploded_view"):
|
||||
table = "`tabBOM Explosion Item`"
|
||||
qty_field = "stock_qty"
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
bom_item.item_code ,
|
||||
bom_item.description ,
|
||||
bom_item.qty,
|
||||
sum(ledger.actual_qty) as actual_qty,
|
||||
sum(FLOOR(ledger.actual_qty /bom_item.qty))as to_build
|
||||
FROM
|
||||
`tabBOM Item` AS bom_item
|
||||
LEFT JOIN `tabBin` AS ledger
|
||||
ON bom_item.item_code = ledger.item_code
|
||||
%s
|
||||
WHERE
|
||||
bom_item.parent = '%s' and bom_item.parenttype='BOM'
|
||||
if filters.get("warehouse"):
|
||||
warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
|
||||
if warehouse_details:
|
||||
conditions += " and exists (select name from `tabWarehouse` wh \
|
||||
where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
|
||||
warehouse_details.rgt)
|
||||
else:
|
||||
conditions += " and ledger.warehouse = '%s'" % frappe.db.escape(filters.get("warehouse"))
|
||||
|
||||
GROUP BY bom_item.item_code""" % (conditions, bom))
|
||||
else:
|
||||
conditions += ""
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
bom_item.item_code ,
|
||||
bom_item.description ,
|
||||
bom_item.{qty_field},
|
||||
sum(ledger.actual_qty) as actual_qty,
|
||||
sum(FLOOR(ledger.actual_qty / bom_item.{qty_field}))as to_build
|
||||
FROM
|
||||
{table} AS bom_item
|
||||
LEFT JOIN `tabBin` AS ledger
|
||||
ON bom_item.item_code = ledger.item_code
|
||||
{conditions}
|
||||
WHERE
|
||||
bom_item.parent = '{bom}' and bom_item.parenttype='BOM'
|
||||
|
||||
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom))
|
||||
|
||||
@@ -498,4 +498,7 @@ erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
|
||||
erpnext.patches.v10_0.update_hub_connector_domain
|
||||
erpnext.patches.v10_0.set_student_party_type
|
||||
erpnext.patches.v10_0.update_project_in_sle
|
||||
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
|
||||
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
|
||||
erpnext.patches.v10_0.taxes_issue_with_pos
|
||||
erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input
|
||||
erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar
|
||||
|
||||
@@ -6,6 +6,7 @@ import frappe
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "bin")
|
||||
frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied")
|
||||
for d in frappe.db.sql("""
|
||||
select distinct rm_item_code, reserve_warehouse
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "stock_settings")
|
||||
|
||||
ss = frappe.get_doc("Stock Settings")
|
||||
ss.set_qty_in_transactions_based_on_serial_no_input = 1
|
||||
|
||||
if ss.default_warehouse \
|
||||
and not frappe.db.exists("Warehouse", ss.default_warehouse):
|
||||
ss.default_warehouse = None
|
||||
|
||||
if ss.stock_uom and not frappe.db.exists("UOM", ss.stock_uom):
|
||||
ss.stock_uom = None
|
||||
|
||||
ss.flags.ignore_mandatory = True
|
||||
ss.save()
|
||||
@@ -0,0 +1,5 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("hr", "doctype", "hr_settings")
|
||||
frappe.db.set_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar", 1)
|
||||
26
erpnext/patches/v10_0/taxes_issue_with_pos.py
Normal file
26
erpnext/patches/v10_0/taxes_issue_with_pos.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for d in frappe.get_all('Sales Invoice', fields=["name"],
|
||||
filters = {'is_pos':1, 'docstatus': 1, 'creation': ('>', '2018-04-23')}):
|
||||
doc = frappe.get_doc('Sales Invoice', d.name)
|
||||
if (not doc.taxes and doc.taxes_and_charges and doc.pos_profile and doc.outstanding_amount != 0 and
|
||||
frappe.db.get_value('POS Profile', doc.pos_profile, 'taxes_and_charges', cache=True) == doc.taxes_and_charges):
|
||||
|
||||
doc.append_taxes_from_master()
|
||||
doc.calculate_taxes_and_totals()
|
||||
for d in doc.taxes:
|
||||
d.db_update()
|
||||
|
||||
doc.db_update()
|
||||
|
||||
delete_gle_for_voucher(doc.name)
|
||||
doc.make_gl_entries(repost_future_gle=False)
|
||||
|
||||
def delete_gle_for_voucher(voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""",
|
||||
{'voucher_no': voucher_no})
|
||||
@@ -2,6 +2,7 @@ import frappe
|
||||
from frappe import _
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('stock', 'doctype', 'item')
|
||||
language = frappe.get_single("System Settings").language
|
||||
|
||||
if language and language.startswith('en'): return
|
||||
|
||||
@@ -75,7 +75,7 @@ class Project(Document):
|
||||
sum = 0
|
||||
for task in self.tasks:
|
||||
if task.task_weight > 0:
|
||||
sum = sum + task.task_weight
|
||||
sum = flt(sum + task.task_weight, task.precision('task_weight'))
|
||||
if sum > 0 and sum != 1:
|
||||
frappe.throw(_("Total of all task weights should be 1. Please adjust weights of all Project tasks accordingly"))
|
||||
|
||||
|
||||
@@ -106,7 +106,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
|
||||
if(
|
||||
this.frm.fields_dict["payment_terms_template"]
|
||||
this.frm.docstatus < 2
|
||||
&& this.frm.fields_dict["payment_terms_template"]
|
||||
&& this.frm.fields_dict["payment_schedule"]
|
||||
&& this.frm.doc.payment_terms_template
|
||||
&& !this.frm.doc.payment_schedule.length
|
||||
@@ -400,7 +401,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
item.serial_no = valid_serial_nos.join('\n');
|
||||
|
||||
refresh_field("serial_no", item.name, item.parentfield);
|
||||
if(!doc.is_return) {
|
||||
if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
|
||||
frappe.model.set_value(item.doctype, item.name,
|
||||
"qty", valid_serial_nos.length / item.conversion_factor);
|
||||
frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
|
||||
@@ -1093,6 +1094,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
me.in_apply_price_list = false;
|
||||
}
|
||||
}
|
||||
}).always(() => {
|
||||
me.in_apply_price_list = false;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -319,7 +319,8 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
|
||||
["parent", "=", $(e.target).attr("data-fieldname")],
|
||||
["attribute_value", "like", e.target.value + "%"]
|
||||
],
|
||||
fields: ["attribute_value"]
|
||||
fields: ["attribute_value"],
|
||||
parent: "Item"
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
|
||||
@@ -10,7 +10,7 @@ def validate_gstin_for_india(doc, method):
|
||||
|
||||
if doc.gstin:
|
||||
doc.gstin = doc.gstin.upper()
|
||||
if doc.gstin != "NA":
|
||||
if doc.gstin not in ["NA", "na"]:
|
||||
p = re.compile("[0-9]{2}[a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9A-Za-z]{1}[Z]{1}[0-9a-zA-Z]{1}")
|
||||
if not p.match(doc.gstin):
|
||||
frappe.throw(_("Invalid GSTIN or Enter NA for Unregistered"))
|
||||
@@ -64,7 +64,7 @@ def get_itemised_tax_breakup_data(doc):
|
||||
def set_place_of_supply(doc, method):
|
||||
if not frappe.get_meta('Address').has_field('gst_state'): return
|
||||
|
||||
if doc.doctype == "Sales Invoice":
|
||||
if doc.doctype in ("Sales Invoice", "Delivery Note"):
|
||||
address_name = doc.shipping_address_name or doc.customer_address
|
||||
elif doc.doctype == "Purchase Invoice":
|
||||
address_name = doc.shipping_address or doc.supplier_address
|
||||
|
||||
@@ -144,7 +144,17 @@ class Gstr1Report(object):
|
||||
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
|
||||
|
||||
for d in items:
|
||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, d.base_net_amount)
|
||||
item_details = {}
|
||||
item_details[d.item_code] = d.base_net_amount
|
||||
|
||||
if d.parent in self.invoice_items:
|
||||
parent_dict = self.invoice_items[d.parent]
|
||||
if d.item_code in parent_dict:
|
||||
item_details[d.item_code] += parent_dict[d.item_code]
|
||||
else:
|
||||
item_details.update(parent_dict)
|
||||
|
||||
self.invoice_items[d.parent] = item_details
|
||||
|
||||
def get_items_based_on_tax_rate(self):
|
||||
self.tax_details = frappe.db.sql("""
|
||||
|
||||
@@ -316,7 +316,7 @@ def make_address(args, is_primary_address=1):
|
||||
return address
|
||||
|
||||
def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters):
|
||||
customer = frappe.db.escape(filters.get('customer'))
|
||||
customer = filters.get('customer')
|
||||
return frappe.db.sql("""
|
||||
select `tabContact`.name from `tabContact`, `tabDynamic Link`
|
||||
where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
|
||||
|
||||
@@ -65,6 +65,10 @@ frappe.ui.form.on("Sales Order Item", {
|
||||
});
|
||||
|
||||
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
|
||||
onload: function(doc, dt, dn) {
|
||||
this._super();
|
||||
},
|
||||
|
||||
refresh: function(doc, dt, dn) {
|
||||
var me = this;
|
||||
this._super();
|
||||
|
||||
@@ -774,7 +774,7 @@ class POSCart {
|
||||
});
|
||||
this.numpad.reset_value();
|
||||
} else {
|
||||
const item_code = this.selected_item.attr('data-item-code');
|
||||
const item_code = unescape(this.selected_item.attr('data-item-code'));
|
||||
const field = this.selected_item.active_field;
|
||||
const value = this.numpad.get_value();
|
||||
|
||||
@@ -819,7 +819,7 @@ class POSCart {
|
||||
}
|
||||
|
||||
update_item(item) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item.item_code)}"]`);
|
||||
|
||||
if(item.qty > 0) {
|
||||
const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
|
||||
@@ -841,7 +841,7 @@ class POSCart {
|
||||
const rate = format_currency(item.rate, this.frm.doc.currency);
|
||||
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
|
||||
return `
|
||||
<div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
|
||||
<div class="list-item indicator ${indicator_class}" data-item-code="${escape(item.item_code)}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
|
||||
<div class="item-name list-item__content list-item__content--flex-1.5 ellipsis">
|
||||
${item.item_name}
|
||||
</div>
|
||||
@@ -883,18 +883,18 @@ class POSCart {
|
||||
}
|
||||
|
||||
exists(item_code) {
|
||||
let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
let $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
return $item.length > 0;
|
||||
}
|
||||
|
||||
highlight_item(item_code) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
$item.addClass('highlight');
|
||||
setTimeout(() => $item.removeClass('highlight'), 1000);
|
||||
}
|
||||
|
||||
scroll_to_item(item_code) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
if ($item.length === 0) return;
|
||||
const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
|
||||
this.$cart_items.animate({ scrollTop });
|
||||
@@ -909,7 +909,7 @@ class POSCart {
|
||||
'[data-action="increment"], [data-action="decrement"]', function() {
|
||||
const $btn = $(this);
|
||||
const $item = $btn.closest('.list-item[data-item-code]');
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
const action = $btn.attr('data-action');
|
||||
|
||||
if(action === 'increment') {
|
||||
@@ -932,7 +932,7 @@ class POSCart {
|
||||
this.$cart_items.on('change', '.quantity input', function() {
|
||||
const $input = $(this);
|
||||
const $item = $input.closest('.list-item[data-item-code]');
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
events.on_field_change(item_code, 'qty', flt($input.val()));
|
||||
});
|
||||
|
||||
@@ -1200,7 +1200,7 @@ class POSItems {
|
||||
var me = this;
|
||||
this.wrapper.on('click', '.pos-item-wrapper', function() {
|
||||
const $item = $(this);
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
me.events.update_cart(item_code, 'qty', '+1');
|
||||
});
|
||||
}
|
||||
@@ -1226,7 +1226,7 @@ class POSItems {
|
||||
const item_title = item_name || item_code;
|
||||
|
||||
const template = `
|
||||
<div class="pos-item-wrapper image-view-item" data-item-code="${item_code}">
|
||||
<div class="pos-item-wrapper image-view-item" data-item-code="${escape(item_code)}">
|
||||
<div class="image-view-header">
|
||||
<div>
|
||||
<a class="grey list-id" data-name="${item_code}" title="${item_title}">
|
||||
|
||||
@@ -49,6 +49,7 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
|
||||
where
|
||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||
and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt})
|
||||
and ifnull(i.end_of_life, curdate()) >= curdate()
|
||||
and {condition}
|
||||
limit {start}, {page_length}""".format(start=start,
|
||||
page_length=page_length, lft=lft, rgt=rgt, condition=condition),
|
||||
|
||||
@@ -122,6 +122,7 @@ class Company(Document):
|
||||
if not wh_detail["is_group"] else ""
|
||||
})
|
||||
warehouse.flags.ignore_permissions = True
|
||||
warehouse.flags.ignore_mandatory = True
|
||||
warehouse.insert()
|
||||
|
||||
def create_default_accounts(self):
|
||||
|
||||
@@ -85,7 +85,7 @@ def delete_lead_addresses(company_name):
|
||||
in ({leads})""".format(leads=",".join(leads)))
|
||||
|
||||
if addresses:
|
||||
addresses = ["'%s'"%addr for addr in addresses]
|
||||
addresses = ["'%s'"%frappe.db.escape(addr) for addr in addresses]
|
||||
|
||||
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
|
||||
name not in (select distinct dl1.parent from `tabDynamic Link` dl1
|
||||
|
||||
@@ -113,8 +113,7 @@ def create_logo(args):
|
||||
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name") ))
|
||||
|
||||
def create_website(args):
|
||||
if args.get('setup_website'):
|
||||
website_maker(args)
|
||||
website_maker(args)
|
||||
|
||||
def get_fy_details(fy_start_date, fy_end_date):
|
||||
start_year = getdate(fy_start_date).year
|
||||
|
||||
@@ -12,7 +12,7 @@ class website_maker(object):
|
||||
self.args = args
|
||||
self.company = args.company_name
|
||||
self.tagline = args.company_tagline
|
||||
self.user = args.name
|
||||
self.user = args.get('email')
|
||||
self.make_web_page()
|
||||
self.make_website_settings()
|
||||
self.make_blog()
|
||||
@@ -50,6 +50,17 @@ class website_maker(object):
|
||||
website_settings.save()
|
||||
|
||||
def make_blog(self):
|
||||
blog_category = frappe.get_doc({
|
||||
"doctype": "Blog Category",
|
||||
"category_name": "general",
|
||||
"published": 1,
|
||||
"title": _("General")
|
||||
}).insert()
|
||||
|
||||
if not self.user:
|
||||
# Admin setup
|
||||
return
|
||||
|
||||
blogger = frappe.new_doc("Blogger")
|
||||
user = frappe.get_doc("User", self.user)
|
||||
blogger.user = self.user
|
||||
@@ -58,13 +69,6 @@ class website_maker(object):
|
||||
blogger.avatar = user.user_image
|
||||
blogger.insert()
|
||||
|
||||
blog_category = frappe.get_doc({
|
||||
"doctype": "Blog Category",
|
||||
"category_name": "general",
|
||||
"published": 1,
|
||||
"title": _("General")
|
||||
}).insert()
|
||||
|
||||
frappe.get_doc({
|
||||
"doctype": "Blog Post",
|
||||
"title": "Welcome",
|
||||
|
||||
@@ -37,6 +37,7 @@ def set_default_settings(args):
|
||||
stock_settings.auto_indent = 1
|
||||
stock_settings.auto_insert_price_list_rate_if_missing = 1
|
||||
stock_settings.automatically_set_serial_nos_based_on_fifo = 1
|
||||
stock_settings.set_qty_in_transactions_based_on_serial_no_input = 1
|
||||
stock_settings.save()
|
||||
|
||||
selling_settings = frappe.get_doc("Selling Settings")
|
||||
|
||||
@@ -88,7 +88,7 @@ erpnext.stock.ItemDashboard = Class.extend({
|
||||
if(!data) data = [];
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production;
|
||||
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
|
||||
d.pending_qty = 0;
|
||||
d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
|
||||
if(d.actual_or_pending > d.actual_qty) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import googlemaps
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.user import get_user_fullname
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils import getdate, cstr
|
||||
from frappe.integrations.doctype.google_maps.google_maps import round_timedelta
|
||||
from frappe.integrations.doctype.google_maps.google_maps import format_address
|
||||
|
||||
@@ -152,17 +152,18 @@ def notify_customers(docname, date, driver, vehicle, sender_email, delivery_noti
|
||||
delivery_stops = frappe.get_all('Delivery Stop', {"parent": docname})
|
||||
attachments = []
|
||||
|
||||
for delivery_stop in delivery_stops:
|
||||
delivery_stop_info = frappe.db.get_value(
|
||||
"Delivery Stop",
|
||||
delivery_stop.name,
|
||||
["notified_by_email", "estimated_arrival", "details", "contact", "delivery_notes"],
|
||||
as_dict=1)
|
||||
contact_info = frappe.db.get_value("Contact", delivery_stop_info.contact,
|
||||
parent_doc = frappe.get_doc('Delivery Trip', docname)
|
||||
args = parent_doc.as_dict()
|
||||
|
||||
for delivery_stop in parent_doc.delivery_stops:
|
||||
contact_info = frappe.db.get_value("Contact", delivery_stop.contact,
|
||||
["first_name", "last_name", "email_id", "gender"], as_dict=1)
|
||||
|
||||
if delivery_stop_info.delivery_notes:
|
||||
delivery_notes = (delivery_stop_info.delivery_notes).split(",")
|
||||
args.update(delivery_stop.as_dict())
|
||||
args.update(contact_info)
|
||||
|
||||
if delivery_stop.delivery_notes:
|
||||
delivery_notes = (delivery_stop.delivery_notes).split(",")
|
||||
default_print_format = frappe.get_meta('Delivery Note').default_print_format
|
||||
attachments = []
|
||||
for delivery_note in delivery_notes:
|
||||
@@ -172,21 +173,14 @@ def notify_customers(docname, date, driver, vehicle, sender_email, delivery_noti
|
||||
file_name="Delivery Note",
|
||||
print_format=default_print_format or "Standard"))
|
||||
|
||||
if not delivery_stop_info.notified_by_email and contact_info.email_id:
|
||||
if not delivery_stop.notified_by_email and contact_info.email_id:
|
||||
driver_info = frappe.db.get_value("Driver", driver, ["full_name", "cell_number"], as_dict=1)
|
||||
sender_designation = frappe.db.get_value("Employee", sender_email, ["designation"])
|
||||
|
||||
estimated_arrival = str(delivery_stop_info.estimated_arrival)[:-3]
|
||||
estimated_arrival = cstr(delivery_stop.estimated_arrival)[:-3]
|
||||
email_template = frappe.get_doc("Standard Reply", delivery_notification)
|
||||
message = frappe.render_template(
|
||||
email_template.response,
|
||||
dict(contact_info=contact_info, sender_name=sender_name,
|
||||
details=delivery_stop_info.details,
|
||||
estimated_arrival=estimated_arrival,
|
||||
date=getdate(date).strftime('%d.%m.%y'), vehicle=vehicle,
|
||||
driver_info=driver_info,
|
||||
sender_designation=sender_designation)
|
||||
)
|
||||
message = frappe.render_template(email_template.response, args)
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=contact_info.email_id,
|
||||
sender=sender_email,
|
||||
|
||||
@@ -457,7 +457,8 @@ $.extend(erpnext.item, {
|
||||
],
|
||||
fields: ["attribute_value"],
|
||||
limit_start: 0,
|
||||
limit_page_length: 500
|
||||
limit_page_length: 500,
|
||||
parent: "Item"
|
||||
}
|
||||
}).then((r) => {
|
||||
if(r.message) {
|
||||
@@ -577,7 +578,8 @@ $.extend(erpnext.item, {
|
||||
["parent","=", i],
|
||||
["attribute_value", "like", term + "%"]
|
||||
],
|
||||
fields: ["attribute_value"]
|
||||
fields: ["attribute_value"],
|
||||
parent: "Item"
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
@@ -236,7 +236,7 @@
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
@@ -3063,7 +3063,7 @@
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
@@ -3561,7 +3561,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 1,
|
||||
"modified": "2018-03-06 10:21:48.715529",
|
||||
"modified": "2018-04-11 12:21:48.715529",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -506,7 +506,7 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def on_trash(self):
|
||||
super(Item, self).on_trash()
|
||||
frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code)
|
||||
frappe.db.sql("""delete from tabBin where item_code=%s""", self.name)
|
||||
frappe.db.sql("delete from `tabItem Price` where item_code=%s", self.name)
|
||||
for variant_of in frappe.get_all("Item", filters={"variant_of": self.name}):
|
||||
frappe.delete_doc("Item", variant_of.name)
|
||||
@@ -604,19 +604,20 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def update_template_item(self):
|
||||
"""Set Show in Website for Template Item if True for its Variant"""
|
||||
if self.variant_of and self.show_in_website:
|
||||
self.show_variant_in_website = 1
|
||||
self.show_in_website = 0
|
||||
if self.variant_of:
|
||||
if self.show_in_website:
|
||||
self.show_variant_in_website = 1
|
||||
self.show_in_website = 0
|
||||
|
||||
if self.show_variant_in_website:
|
||||
# show template
|
||||
template_item = frappe.get_doc("Item", self.variant_of)
|
||||
if self.show_variant_in_website:
|
||||
# show template
|
||||
template_item = frappe.get_doc("Item", self.variant_of)
|
||||
|
||||
if not template_item.show_in_website:
|
||||
template_item.show_in_website = 1
|
||||
template_item.flags.dont_update_variants = True
|
||||
template_item.flags.ignore_permissions = True
|
||||
template_item.save()
|
||||
if not template_item.show_in_website:
|
||||
template_item.show_in_website = 1
|
||||
template_item.flags.dont_update_variants = True
|
||||
template_item.flags.ignore_permissions = True
|
||||
template_item.save()
|
||||
|
||||
def update_variants(self):
|
||||
if self.flags.dont_update_variants or \
|
||||
@@ -640,9 +641,9 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def validate_stock_exists_for_template_item(self):
|
||||
if self.stock_ledger_created() and self._doc_before_save:
|
||||
if (self._doc_before_save.has_variants != self.has_variants
|
||||
if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
|
||||
or self._doc_before_save.variant_of != self.variant_of):
|
||||
frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),
|
||||
frappe.throw(_("Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.").format(self.name),
|
||||
StockExistsForTemplate)
|
||||
|
||||
if self.has_variants or self.variant_of:
|
||||
|
||||
@@ -417,6 +417,7 @@ def raise_production_orders(material_request):
|
||||
prod_order.material_request_item = d.name
|
||||
prod_order.planned_start_date = mr.transaction_date
|
||||
prod_order.company = mr.company
|
||||
prod_order.set_production_order_operations()
|
||||
prod_order.save()
|
||||
production_orders.append(prod_order.name)
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,7 @@ def get_data():
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Related'),
|
||||
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order']
|
||||
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', "Stock Entry"]
|
||||
},
|
||||
{
|
||||
'label': _('Manufacturing'),
|
||||
|
||||
@@ -198,7 +198,7 @@ def validate_serial_no(sle, item_det):
|
||||
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
|
||||
|
||||
if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
|
||||
frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(sle.actual_qty, sle.item_code, len(serial_nos)),
|
||||
frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(abs(sle.actual_qty), sle.item_code, len(serial_nos)),
|
||||
SerialNoQtyError)
|
||||
|
||||
if len(serial_nos) != len(set(serial_nos)):
|
||||
|
||||
@@ -463,6 +463,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "set_qty_in_transactions_based_on_serial_no_input",
|
||||
"fieldtype": "Check",
|
||||
"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": "Set Qty in Transactions based on Serial No Input",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -679,7 +710,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-17 01:35:49.562613",
|
||||
"modified": "2018-05-03 12:37:12.905394",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Settings",
|
||||
|
||||
@@ -11,8 +11,9 @@ from frappe.utils.html_utils import clean_html
|
||||
|
||||
class StockSettings(Document):
|
||||
def validate(self):
|
||||
for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock", "default_warehouse"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
for key in ["item_naming_by", "item_group", "stock_uom",
|
||||
"allow_negative_stock", "default_warehouse", "set_qty_in_transactions_based_on_serial_no_input"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
|
||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||
set_by_naming_series("Item", "item_code",
|
||||
|
||||
@@ -319,7 +319,7 @@ def get_price_list_rate(args, item_doc, out):
|
||||
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
|
||||
/ flt(args.conversion_rate)
|
||||
if not args.price_list_uom_dependant:
|
||||
out.price_list_rate = flt(out.price_list_rate * (args.conversion_factor or 1.0))
|
||||
out.price_list_rate = flt(out.price_list_rate * (flt(args.conversion_factor) or 1.0))
|
||||
|
||||
if not out.price_list_rate and args.transaction_type=="buying":
|
||||
from erpnext.stock.doctype.item.item import get_last_purchase_details
|
||||
@@ -410,7 +410,7 @@ def get_party_item_code(args, item_doc, out):
|
||||
item_supplier = item_doc.get("supplier_items", {"supplier": args.supplier})
|
||||
out.supplier_part_no = item_supplier[0].supplier_part_no if item_supplier else None
|
||||
|
||||
def get_pos_profile_item_details(company, args, pos_profile=None):
|
||||
def get_pos_profile_item_details(company, args, pos_profile=None, update_data=False):
|
||||
res = frappe._dict()
|
||||
|
||||
if not pos_profile:
|
||||
@@ -418,7 +418,7 @@ def get_pos_profile_item_details(company, args, pos_profile=None):
|
||||
|
||||
if pos_profile:
|
||||
for fieldname in ("income_account", "cost_center", "warehouse", "expense_account"):
|
||||
if not args.get(fieldname) and pos_profile.get(fieldname):
|
||||
if (not args.get(fieldname) or update_data) and pos_profile.get(fieldname):
|
||||
res[fieldname] = pos_profile.get(fieldname)
|
||||
|
||||
if res.get("warehouse"):
|
||||
|
||||
@@ -14,10 +14,15 @@ def get_data(item):
|
||||
if not item:
|
||||
return []
|
||||
item_dicts = []
|
||||
variants = None
|
||||
|
||||
variant_results = frappe.db.sql("""select name from `tabItem`
|
||||
where variant_of = %s""", item, as_dict=1)
|
||||
variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results])
|
||||
if not variant_results:
|
||||
frappe.msgprint(_("There isn't any item variant for the selected item"))
|
||||
return []
|
||||
else:
|
||||
variants = ",".join(['"' + frappe.db.escape(variant['name']) + '"' for variant in variant_results])
|
||||
|
||||
order_count_map = get_open_sales_orders_map(variants)
|
||||
stock_details_map = get_stock_details_map(variants)
|
||||
@@ -35,7 +40,9 @@ def get_data(item):
|
||||
name = item_dict["variant_name"]
|
||||
|
||||
for d in attribute_list:
|
||||
item_dict[d] = attr_val_map[name][d]
|
||||
attr_dict = attr_val_map[name]
|
||||
if attr_dict and attr_dict.get(d):
|
||||
item_dict[d] = attr_val_map[name][d]
|
||||
|
||||
item_dict["Open Orders"] = order_count_map.get(name) or 0
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ frappe.query_reports["Stock Balance"] = {
|
||||
"fieldtype": "Date",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"default": frappe.sys_defaults.year_start_date,
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
@@ -31,7 +31,12 @@ frappe.query_reports["Stock Balance"] = {
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"get_query": function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint, getdate
|
||||
from frappe.utils import flt, cint, getdate, now
|
||||
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
@@ -12,10 +13,11 @@ def execute(filters=None):
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns()
|
||||
item_map = get_item_details(filters)
|
||||
item_reorder_detail_map = get_item_reorder_details(filters)
|
||||
iwb_map = get_item_warehouse_map(filters)
|
||||
|
||||
items = get_items(filters)
|
||||
sle = get_stock_ledger_entries(filters, items)
|
||||
iwb_map = get_item_warehouse_map(filters, sle)
|
||||
item_map = get_item_details(items, sle, filters)
|
||||
item_reorder_detail_map = get_item_reorder_details(item_map.keys())
|
||||
|
||||
data = []
|
||||
for (company, item, warehouse) in sorted(iwb_map):
|
||||
@@ -88,21 +90,9 @@ def get_conditions(filters):
|
||||
else:
|
||||
frappe.throw(_("'To Date' is required"))
|
||||
|
||||
if filters.get("item_group"):
|
||||
ig_details = frappe.db.get_value("Item Group", filters.get("item_group"),
|
||||
["lft", "rgt"], as_dict=1)
|
||||
|
||||
if ig_details:
|
||||
conditions += """
|
||||
and exists (select name from `tabItem Group` ig
|
||||
where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)
|
||||
""" % (ig_details.lft, ig_details.rgt)
|
||||
|
||||
if filters.get("item_code"):
|
||||
conditions += " and sle.item_code = '%s'" % frappe.db.escape(filters.get("item_code"), percent=False)
|
||||
|
||||
if filters.get("warehouse"):
|
||||
warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
|
||||
warehouse_details = frappe.db.get_value("Warehouse",
|
||||
filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
|
||||
if warehouse_details:
|
||||
conditions += " and exists (select name from `tabWarehouse` wh \
|
||||
where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft,
|
||||
@@ -110,30 +100,29 @@ def get_conditions(filters):
|
||||
|
||||
return conditions
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
def get_stock_ledger_entries(filters, items):
|
||||
item_conditions_sql = ''
|
||||
if items:
|
||||
item_conditions_sql = ' and sle.item_code in ({})'\
|
||||
.format(', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in items]))
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
join_table_query = ""
|
||||
if filters.get("item_group"):
|
||||
join_table_query = "inner join `tabItem` item on item.name = sle.item_code"
|
||||
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate,
|
||||
sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference
|
||||
from
|
||||
`tabStock Ledger Entry` sle force index (posting_sort_index) %s
|
||||
where sle.docstatus < 2 %s
|
||||
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
||||
where sle.docstatus < 2 %s %s
|
||||
order by sle.posting_date, sle.posting_time, sle.name""" %
|
||||
(join_table_query, conditions), as_dict=1)
|
||||
(item_conditions_sql, conditions), as_dict=1)
|
||||
|
||||
def get_item_warehouse_map(filters):
|
||||
def get_item_warehouse_map(filters, sle):
|
||||
iwb_map = {}
|
||||
from_date = getdate(filters.get("from_date"))
|
||||
to_date = getdate(filters.get("to_date"))
|
||||
|
||||
sle = get_stock_ledger_entries(filters)
|
||||
|
||||
for d in sle:
|
||||
key = (d.company, d.item_code, d.warehouse)
|
||||
if key not in iwb_map:
|
||||
@@ -191,20 +180,34 @@ def filter_items_with_no_transactions(iwb_map):
|
||||
|
||||
return iwb_map
|
||||
|
||||
def get_item_details(filters):
|
||||
condition = ''
|
||||
value = ()
|
||||
def get_items(filters):
|
||||
conditions = []
|
||||
if filters.get("item_code"):
|
||||
condition = "where item_code=%s"
|
||||
value = (filters.get("item_code"),)
|
||||
conditions.append("item.name=%(item_code)s")
|
||||
else:
|
||||
if filters.get("brand"):
|
||||
conditions.append("item.brand=%(brand)s")
|
||||
if filters.get("item_group"):
|
||||
conditions.append(get_item_group_condition(filters.get("item_group")))
|
||||
|
||||
items = frappe.db.sql("""
|
||||
select name, item_name, stock_uom, item_group, brand, description
|
||||
from tabItem
|
||||
{condition}
|
||||
""".format(condition=condition), value, as_dict=1)
|
||||
items = []
|
||||
if conditions:
|
||||
items = frappe.db.sql_list("""select name from `tabItem` item where {}"""
|
||||
.format(" and ".join(conditions)), filters)
|
||||
return items
|
||||
|
||||
item_details = dict((d.name , d) for d in items)
|
||||
def get_item_details(items, sle, filters):
|
||||
item_details = {}
|
||||
if not items:
|
||||
items = list(set([d.item_code for d in sle]))
|
||||
|
||||
if items:
|
||||
for item in frappe.db.sql("""
|
||||
select name, item_name, description, item_group, brand, stock_uom
|
||||
from `tabItem`
|
||||
where name in ({0})
|
||||
""".format(', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in items])), as_dict=1):
|
||||
item_details.setdefault(item.name, item)
|
||||
|
||||
if filters.get('show_variant_attributes', 0) == 1:
|
||||
variant_values = get_variant_values_for(item_details.keys())
|
||||
@@ -212,18 +215,15 @@ def get_item_details(filters):
|
||||
|
||||
return item_details
|
||||
|
||||
def get_item_reorder_details(filters):
|
||||
condition = ''
|
||||
value = ()
|
||||
if filters.get("item_code"):
|
||||
condition = "where parent=%s"
|
||||
value = (filters.get("item_code"),)
|
||||
def get_item_reorder_details(items):
|
||||
item_reorder_details = frappe._dict()
|
||||
|
||||
item_reorder_details = frappe.db.sql("""
|
||||
select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level
|
||||
from `tabItem Reorder`
|
||||
{condition}
|
||||
""".format(condition=condition), value, as_dict=1)
|
||||
if items:
|
||||
item_reorder_details = frappe.db.sql("""
|
||||
select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level
|
||||
from `tabItem Reorder`
|
||||
where parent in ({0})
|
||||
""".format(', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in items])), as_dict=1)
|
||||
|
||||
return dict((d.parent + d.warehouse, d) for d in item_reorder_details)
|
||||
|
||||
@@ -233,12 +233,10 @@ def validate_filters(filters):
|
||||
if sle_count > 500000:
|
||||
frappe.throw(_("Please set filter based on Item or Warehouse"))
|
||||
|
||||
|
||||
def get_variants_attributes():
|
||||
'''Return all item variant attributes.'''
|
||||
return [i.name for i in frappe.get_all('Item Attribute')]
|
||||
|
||||
|
||||
def get_variant_values_for(items):
|
||||
'''Returns variant values for items.'''
|
||||
attribute_map = {}
|
||||
|
||||
@@ -35,7 +35,12 @@ frappe.query_reports["Stock Ledger"] = {
|
||||
"fieldname":"item_code",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"get_query": function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"item_group",
|
||||
|
||||
@@ -7,12 +7,12 @@ from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns()
|
||||
sl_entries = get_stock_ledger_entries(filters)
|
||||
item_details = get_item_details(filters)
|
||||
items = get_items(filters)
|
||||
sl_entries = get_stock_ledger_entries(filters, items)
|
||||
item_details = get_item_details(items, sl_entries)
|
||||
opening_row = get_opening_balance(filters, columns)
|
||||
|
||||
data = []
|
||||
|
||||
if opening_row:
|
||||
data.append(opening_row)
|
||||
|
||||
@@ -52,7 +52,12 @@ def get_columns():
|
||||
|
||||
return columns
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
def get_stock_ledger_entries(filters, items):
|
||||
item_conditions_sql = ''
|
||||
if items:
|
||||
item_conditions_sql = 'and sle.item_code in ({})'\
|
||||
.format(', '.join(['"' + frappe.db.escape(i,percent=False) + '"' for i in items]))
|
||||
|
||||
return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date,
|
||||
item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate,
|
||||
stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project
|
||||
@@ -60,35 +65,48 @@ def get_stock_ledger_entries(filters):
|
||||
where company = %(company)s and
|
||||
posting_date between %(from_date)s and %(to_date)s
|
||||
{sle_conditions}
|
||||
{item_conditions_sql}
|
||||
order by posting_date asc, posting_time asc, name asc"""\
|
||||
.format(sle_conditions=get_sle_conditions(filters)), filters, as_dict=1)
|
||||
.format(
|
||||
sle_conditions=get_sle_conditions(filters),
|
||||
item_conditions_sql = item_conditions_sql
|
||||
), filters, as_dict=1)
|
||||
|
||||
def get_item_details(filters):
|
||||
item_details = {}
|
||||
for item in frappe.db.sql("""select name, item_name, description, item_group,
|
||||
brand, stock_uom from `tabItem` item {item_conditions}"""\
|
||||
.format(item_conditions=get_item_conditions(filters)), filters, as_dict=1):
|
||||
item_details.setdefault(item.name, item)
|
||||
|
||||
return item_details
|
||||
|
||||
def get_item_conditions(filters):
|
||||
def get_items(filters):
|
||||
conditions = []
|
||||
if filters.get("item_code"):
|
||||
conditions.append("item.name=%(item_code)s")
|
||||
if filters.get("brand"):
|
||||
conditions.append("item.brand=%(brand)s")
|
||||
if filters.get("item_group"):
|
||||
conditions.append(get_item_group_condition(filters.get("item_group")))
|
||||
else:
|
||||
if filters.get("brand"):
|
||||
conditions.append("item.brand=%(brand)s")
|
||||
if filters.get("item_group"):
|
||||
conditions.append(get_item_group_condition(filters.get("item_group")))
|
||||
|
||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
||||
items = []
|
||||
if conditions:
|
||||
items = frappe.db.sql_list("""select name from `tabItem` item where {}"""
|
||||
.format(" and ".join(conditions)), filters)
|
||||
return items
|
||||
|
||||
def get_item_details(items, sl_entries):
|
||||
item_details = {}
|
||||
if not items:
|
||||
items = list(set([d.item_code for d in sl_entries]))
|
||||
|
||||
if not items:
|
||||
return item_details
|
||||
|
||||
for item in frappe.db.sql("""
|
||||
select name, item_name, description, item_group, brand, stock_uom
|
||||
from `tabItem`
|
||||
where name in ({0})
|
||||
""".format(', '.join(['"' + frappe.db.escape(i,percent=False) + '"' for i in items])), as_dict=1):
|
||||
item_details.setdefault(item.name, item)
|
||||
|
||||
return item_details
|
||||
|
||||
def get_sle_conditions(filters):
|
||||
conditions = []
|
||||
item_conditions=get_item_conditions(filters)
|
||||
if item_conditions:
|
||||
conditions.append("""sle.item_code in (select item.name from tabItem item
|
||||
{item_conditions})""".format(item_conditions=item_conditions))
|
||||
if filters.get("warehouse"):
|
||||
warehouse_condition = get_warehouse_condition(filters.get("warehouse"))
|
||||
if warehouse_condition:
|
||||
|
||||
@@ -19,7 +19,12 @@ frappe.query_reports["Stock Projected Qty"] = {
|
||||
"fieldname":"item_code",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"get_query": function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"brand",
|
||||
|
||||
@@ -442,7 +442,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and warehouse = %s
|
||||
and valuation_rate > 0
|
||||
and valuation_rate >= 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse))
|
||||
|
||||
if not last_valuation_rate:
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -71,6 +72,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -100,6 +102,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -131,6 +134,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -159,6 +163,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -192,6 +197,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -225,6 +231,39 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "email_account",
|
||||
"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": "Email Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Email Account",
|
||||
"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
|
||||
},
|
||||
{
|
||||
@@ -257,6 +296,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -287,6 +327,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -319,6 +360,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -349,6 +391,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -379,6 +422,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -408,6 +452,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -438,6 +483,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -468,6 +514,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -498,6 +545,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -527,6 +575,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -558,6 +607,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -589,6 +639,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -619,6 +670,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -649,6 +701,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -681,6 +734,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -711,6 +765,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -743,6 +798,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -774,6 +830,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -806,6 +863,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -835,6 +893,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
@@ -865,6 +924,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
@@ -879,7 +939,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-01-11 07:10:53.707415",
|
||||
"modified": "2018-04-13 13:03:14.748090",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Support",
|
||||
"name": "Issue",
|
||||
@@ -887,7 +947,6 @@
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
<tr>
|
||||
<td>{{ item }}</td>
|
||||
<td class='text-right'>
|
||||
{{ frappe.utils.fmt_money(itemised_taxable_amount.get(item), None, company_currency) }}
|
||||
{{ frappe.utils.fmt_money(itemised_taxable_amount.get(item), None, currency) }}
|
||||
</td>
|
||||
{% for tax_account in tax_accounts %}
|
||||
{% set tax_details = taxes.get(tax_account) %}
|
||||
{% if tax_details %}
|
||||
<td class='text-right'>
|
||||
{% if tax_details.tax_rate or not tax_details.tax_amount %}
|
||||
({{ tax_details.tax_rate }}%)
|
||||
({{ tax_details.tax_rate }}%)
|
||||
{% endif %}
|
||||
{{ frappe.utils.fmt_money(tax_details.tax_amount, None, company_currency) }}
|
||||
{{ frappe.utils.fmt_money(tax_details.tax_amount / conversion_rate, None, currency) }}
|
||||
</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<a class="product-link" href="{{ item.route|abs_url }}">
|
||||
<div class="col-sm-4 col-xs-4 product-image-wrapper">
|
||||
<div class="product-image-img">
|
||||
{{ product_image_square(item.thumbnail or item.image) }}
|
||||
<!-- thumbnail not updated, and used as background image in item card -->
|
||||
{{ product_image_square(item.image) }}
|
||||
<div class="product-text" itemprop="name">{{ item.item_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ frappe.Leaderboard = Class.extend({
|
||||
"Item": ["total_sales_amount", "total_qty_sold", "total_purchase_amount",
|
||||
"total_qty_purchased", "available_stock_qty", "available_stock_value"],
|
||||
"Supplier": ["total_purchase_amount", "total_qty_purchased", "outstanding_amount"],
|
||||
"Sales Partner": ["total_sales_amount", "total_commision"],
|
||||
"Sales Partner": ["total_sales_amount", "total_commission"],
|
||||
"Sales Person": ["total_sales_amount"],
|
||||
};
|
||||
|
||||
|
||||
13
setup.py
13
setup.py
@@ -1,16 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from setuptools import setup, find_packages
|
||||
from pip.req import parse_requirements
|
||||
import re, ast
|
||||
|
||||
# get version from __version__ variable in erpnext/__init__.py
|
||||
_version_re = re.compile(r'__version__\s+=\s+(.*)')
|
||||
|
||||
with open('erpnext/__init__.py', 'rb') as f:
|
||||
version = str(ast.literal_eval(_version_re.search(
|
||||
f.read().decode('utf-8')).group(1)))
|
||||
with open('requirements.txt') as f:
|
||||
install_requires = f.read().strip().split('\n')
|
||||
|
||||
requirements = parse_requirements("requirements.txt", session="")
|
||||
with open('erpnext/__init__.py', 'rb') as f:
|
||||
version = str(ast.literal_eval(_version_re.search(
|
||||
f.read().decode('utf-8')).group(1)))
|
||||
|
||||
setup(
|
||||
name='erpnext',
|
||||
@@ -21,6 +21,5 @@ setup(
|
||||
packages=find_packages(),
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
install_requires=[str(ir.req) for ir in requirements],
|
||||
dependency_links=[str(ir._link) for ir in requirements if ir._link]
|
||||
install_requires=install_requires
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user