mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-20 03:12:32 +00:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc35138356 | ||
|
|
08617f7700 | ||
|
|
cfecf3c5b1 | ||
|
|
831100b657 | ||
|
|
f296a2f58a | ||
|
|
aae557e8a1 | ||
|
|
0f92c4b85b | ||
|
|
217df1761d | ||
|
|
d355741462 | ||
|
|
7b6ed4f16e | ||
|
|
c5a682e9d9 | ||
|
|
b4fa105555 | ||
|
|
e1aef6f0db | ||
|
|
6f35d2b52d | ||
|
|
197f57c050 | ||
|
|
c74fa7546b | ||
|
|
233ef8752d | ||
|
|
afd9e7546f | ||
|
|
8b1532d691 | ||
|
|
a887c29e06 | ||
|
|
7114b72be8 | ||
|
|
92ab345daf | ||
|
|
79dd4d707b | ||
|
|
a3a733fa91 | ||
|
|
7a9b014f72 | ||
|
|
61b4a98356 | ||
|
|
46db69b304 | ||
|
|
a0be41322d | ||
|
|
05f29988f4 | ||
|
|
d7903de51c | ||
|
|
0e31cc9ff8 | ||
|
|
32280a11aa | ||
|
|
a2f9d2f89c | ||
|
|
5fc5355b96 | ||
|
|
30c73e6b35 | ||
|
|
7a6b88c791 | ||
|
|
9c42124fbc | ||
|
|
0378b15412 | ||
|
|
3f86369604 | ||
|
|
cc99d92116 | ||
|
|
f880975438 | ||
|
|
daa8db4d95 | ||
|
|
9ff39e2d1b | ||
|
|
9d70c5d13f | ||
|
|
a9a82919e7 | ||
|
|
723ed07642 | ||
|
|
86b531aea6 | ||
|
|
781a420593 | ||
|
|
08f709c2a2 | ||
|
|
a849f6e21e | ||
|
|
6ff8387d56 | ||
|
|
2af9ff9c33 | ||
|
|
01905cad2f | ||
|
|
58260e4f0a | ||
|
|
a01869bb20 | ||
|
|
3a92615f23 | ||
|
|
eca8db7405 | ||
|
|
58c4cfc0d7 | ||
|
|
ab52a4db75 | ||
|
|
c8d3a8c0f5 | ||
|
|
aab88eee3e | ||
|
|
d9f4c83567 | ||
|
|
b96dd366ce | ||
|
|
597ec83af7 | ||
|
|
e802bdd186 | ||
|
|
3a949bb298 | ||
|
|
1e920dd0d8 | ||
|
|
09536f402e | ||
|
|
581f26b7a0 | ||
|
|
0418a2f70a | ||
|
|
5e3338744c | ||
|
|
da3762700c | ||
|
|
2c9fccd8ba | ||
|
|
286c4fa640 | ||
|
|
a144e002db | ||
|
|
553dabaa08 | ||
|
|
55d0d32c77 | ||
|
|
1f93745eef | ||
|
|
e3a02dd5f6 | ||
|
|
153733414f | ||
|
|
ac535f0ce9 | ||
|
|
3886529787 | ||
|
|
d2cd713b89 | ||
|
|
6b378e1669 | ||
|
|
a4fc30bbe4 | ||
|
|
b4b0e4424d | ||
|
|
f3bdcc2a84 | ||
|
|
19901c14c9 | ||
|
|
714d686e50 | ||
|
|
094dc1dee6 | ||
|
|
cdcf424ba5 | ||
|
|
2a2b884e32 | ||
|
|
c1e00f4daa | ||
|
|
0a22aab6bb | ||
|
|
43c6d1a518 | ||
|
|
090219814e | ||
|
|
510dc60bf0 | ||
|
|
12d520a366 | ||
|
|
a06a527fe6 | ||
|
|
9a7681535f | ||
|
|
8e1a612b3b | ||
|
|
21085bf2be | ||
|
|
8e9413829d | ||
|
|
8208f878ff | ||
|
|
a7130649cd | ||
|
|
b2d9ffa4ca | ||
|
|
cc4f13a862 | ||
|
|
60681baf80 | ||
|
|
ea6049078a | ||
|
|
d83e8c56b2 | ||
|
|
e0ade62e38 | ||
|
|
c78b921412 | ||
|
|
9e9e415c5f | ||
|
|
c87f8c6f00 | ||
|
|
a970bcc56d | ||
|
|
5e2d822509 | ||
|
|
8908d4ee8f | ||
|
|
d62de6c8e5 | ||
|
|
f62e789173 | ||
|
|
91578757ff |
20
MANIFEST.in
20
MANIFEST.in
@@ -1,20 +0,0 @@
|
||||
include MANIFEST.in
|
||||
include requirements.txt
|
||||
include *.json
|
||||
include *.md
|
||||
include *.py
|
||||
include *.txt
|
||||
include .travis.yml
|
||||
recursive-include erpnext *.txt
|
||||
recursive-include erpnext *.css
|
||||
recursive-include erpnext *.csv
|
||||
recursive-include erpnext *.html
|
||||
recursive-include erpnext *.ico
|
||||
recursive-include erpnext *.js
|
||||
recursive-include erpnext *.json
|
||||
recursive-include erpnext *.md
|
||||
recursive-include erpnext *.png
|
||||
recursive-include erpnext *.py
|
||||
recursive-include erpnext *.svg
|
||||
recursive-include erpnext/public *
|
||||
recursive-exclude * *.pyc
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '11.1.30'
|
||||
__version__ = '11.1.35'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.validate_fixed_asset()
|
||||
self.create_remarks()
|
||||
self.set_status()
|
||||
self.validate_purchase_receipt_if_update_stock()
|
||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||
|
||||
def validate_release_date(self):
|
||||
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
'target_parent_field': 'per_received',
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'qty',
|
||||
'source_field': 'received_qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': 'received_qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'percent_join_field':'purchase_order',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
# 'target_parent_field': 'per_received',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
# 'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def validate_purchase_receipt_if_update_stock(self):
|
||||
if self.update_stock:
|
||||
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.check_prev_docstatus()
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.base_grand_total)
|
||||
|
||||
if not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
@@ -763,13 +765,13 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_for_closed_status()
|
||||
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
if not self.is_return:
|
||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||
unlink_ref_doc_from_payment_entries(self)
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return", "release_date", "on_hold"],
|
||||
get_indicator: function(doc) {
|
||||
if(cint(doc.is_return)==1) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||
if(cint(doc.on_hold) && !doc.release_date) {
|
||||
return [__("On Hold"), "darkgrey"];
|
||||
@@ -18,9 +18,9 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
|
||||
}
|
||||
} else if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
}else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def set_indicator(self):
|
||||
"""Set indicator for portal"""
|
||||
if cint(self.is_return) == 1:
|
||||
self.indicator_title = _("Return")
|
||||
if self.outstanding_amount < 0:
|
||||
self.indicator_title = _("Credit Note Issued")
|
||||
self.indicator_color = "darkgrey"
|
||||
elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()):
|
||||
self.indicator_color = "orange"
|
||||
@@ -63,8 +63,8 @@ class SalesInvoice(SellingController):
|
||||
elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()):
|
||||
self.indicator_color = "red"
|
||||
self.indicator_title = _("Overdue")
|
||||
elif self.outstanding_amount < 0:
|
||||
self.indicator_title = _("Credit Note Issued")
|
||||
elif cint(self.is_return) == 1:
|
||||
self.indicator_title = _("Return")
|
||||
self.indicator_color = "darkgrey"
|
||||
else:
|
||||
self.indicator_color = "green"
|
||||
@@ -256,7 +256,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt':'Sales Invoice Item',
|
||||
'target_dt':'Sales Order Item',
|
||||
'target_parent_dt':'Sales Order',
|
||||
@@ -274,21 +274,20 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
||||
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
# 'target_parent_field': 'per_delivered',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'sales_order',
|
||||
# 'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Delivery Note Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'so_detail',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def check_credit_limit(self):
|
||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||
@@ -504,11 +503,14 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def so_dn_required(self):
|
||||
"""check in manage account if sales order / delivery note required or not."""
|
||||
if self.is_return:
|
||||
return
|
||||
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
|
||||
for i in dic:
|
||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||
for d in self.get('items'):
|
||||
if (d.item_code and frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1
|
||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||
if (d.item_code and is_stock_item == 1\
|
||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||
|
||||
@@ -1167,6 +1169,8 @@ class SalesInvoice(SellingController):
|
||||
self.set_missing_values(for_validate = True)
|
||||
|
||||
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
|
||||
if not party:
|
||||
return
|
||||
if doctype == "Sales Invoice":
|
||||
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
|
||||
ref_doc = "Purchase Invoice"
|
||||
|
||||
@@ -6,16 +6,16 @@ frappe.listview_settings['Sales Invoice'] = {
|
||||
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return"],
|
||||
get_indicator: function(doc) {
|
||||
if(cint(doc.is_return)==1) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"]
|
||||
} else if(flt(doc.outstanding_amount) < 0) {
|
||||
if(flt(doc.outstanding_amount) < 0) {
|
||||
return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
}else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"]
|
||||
}
|
||||
},
|
||||
right_column: "grand_total"
|
||||
|
||||
@@ -314,13 +314,11 @@ class Subscription(Document):
|
||||
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def is_postpaid_to_invoice(self):
|
||||
return getdate(nowdate()) > getdate(self.current_invoice_end) or \
|
||||
(getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \
|
||||
not self.has_outstanding_invoice()
|
||||
|
||||
@property
|
||||
def is_prepaid_to_invoice(self):
|
||||
if not self.generate_invoice_at_period_start:
|
||||
return False
|
||||
@@ -340,7 +338,7 @@ class Subscription(Document):
|
||||
2. Change the `Subscription` status to 'Past Due Date'
|
||||
3. Change the `Subscription` status to 'Cancelled'
|
||||
"""
|
||||
if self.is_postpaid_to_invoice or self.is_prepaid_to_invoice:
|
||||
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
|
||||
self.generate_invoice()
|
||||
if self.current_invoice_is_past_due():
|
||||
self.status = 'Past Due Date'
|
||||
|
||||
@@ -77,7 +77,7 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
|
||||
payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
|
||||
"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
|
||||
["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||
["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||
|
||||
if transaction.credit > 0:
|
||||
journal_entries = frappe.db.sql("""
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -100,11 +100,14 @@ class ReceivablePayableReport(object):
|
||||
self.filters["range2"] = "60"
|
||||
if not "range3" in self.filters:
|
||||
self.filters["range3"] = "90"
|
||||
if not "range4" in self.filters:
|
||||
self.filters["range4"] = "120"
|
||||
|
||||
for label in ("0-{range1}".format(range1=self.filters["range1"]),
|
||||
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
|
||||
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
|
||||
"{range3}-{above}".format(range3=cint(self.filters["range3"])+ 1, above=_("Above"))):
|
||||
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))):
|
||||
columns.append({
|
||||
"label": label,
|
||||
"fieldname":label,
|
||||
@@ -328,18 +331,17 @@ class ReceivablePayableReport(object):
|
||||
entry_date = gle.posting_date
|
||||
|
||||
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
|
||||
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
cint(self.filters.range3), cint(self.filters.range4), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
# issue 6371-Ageing buckets should not have amounts if due date is not reached
|
||||
if self.filters.ageing_based_on == "Due Date" \
|
||||
and getdate(due_date) > getdate(self.filters.report_date):
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
|
||||
|
||||
if self.filters.ageing_based_on == "Supplier Invoice Date" \
|
||||
and getdate(bill_date) > getdate(self.filters.report_date):
|
||||
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
|
||||
|
||||
if self.filters.get(scrub(args.get("party_type"))):
|
||||
row.append(gle.account_currency)
|
||||
@@ -585,13 +587,13 @@ class ReceivablePayableReport(object):
|
||||
return payment_term_map
|
||||
|
||||
def get_chart_data(self, columns, data):
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
||||
|
||||
rows = []
|
||||
for d in data:
|
||||
rows.append(
|
||||
{
|
||||
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -610,21 +612,22 @@ def execute(filters=None):
|
||||
}
|
||||
return ReceivablePayableReport(filters).run(args)
|
||||
|
||||
def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_date, outstanding_amount):
|
||||
# [0-30, 30-60, 60-90, 90-above]
|
||||
outstanding_range = [0.0, 0.0, 0.0, 0.0]
|
||||
def get_ageing_data(first_range, second_range, third_range,
|
||||
fourth_range, age_as_on, entry_date, outstanding_amount):
|
||||
# [0-30, 30-60, 60-90, 90-120, 120-above]
|
||||
outstanding_range = [0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
|
||||
if not (age_as_on and entry_date):
|
||||
return [0] + outstanding_range
|
||||
|
||||
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
|
||||
index = None
|
||||
for i, days in enumerate([first_range, second_range, third_range]):
|
||||
for i, days in enumerate([first_range, second_range, third_range, fourth_range]):
|
||||
if age <= days:
|
||||
index = i
|
||||
break
|
||||
|
||||
if index is None: index = 3
|
||||
if index is None: index = 4
|
||||
outstanding_range[index] = outstanding_amount
|
||||
|
||||
return [age] + outstanding_range
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -82,8 +82,15 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range3) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range3) + _("-Above")),
|
||||
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range4) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
@@ -152,7 +159,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
|
||||
row += [
|
||||
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
@@ -178,6 +185,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0,
|
||||
"range5": 0,
|
||||
"sales_person": []
|
||||
})
|
||||
)
|
||||
@@ -209,7 +217,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
cols += ["bill_no", "bill_date"]
|
||||
|
||||
cols += ["invoiced_amt", "paid_amt", "credit_amt",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"pdc/lc_amount"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
|
||||
@@ -321,7 +321,10 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
||||
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
||||
|
||||
def compare_accounts(a, b):
|
||||
if is_root:
|
||||
if re.split('\W+', a[key])[0].isdigit():
|
||||
# if chart of accounts is numbered, then sort by number
|
||||
return cmp(a[key], b[key])
|
||||
elif is_root:
|
||||
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
||||
return -1
|
||||
if a.root_type != b.root_type and a.root_type == "Asset":
|
||||
@@ -330,10 +333,6 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
||||
return -1
|
||||
if a.root_type == "Income" and b.root_type == "Expense":
|
||||
return -1
|
||||
else:
|
||||
if re.split('\W+', a[key])[0].isdigit():
|
||||
# if chart of accounts is numbered, then sort by number
|
||||
return cmp(a[key], b[key])
|
||||
return 1
|
||||
|
||||
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
||||
|
||||
@@ -211,6 +211,11 @@ frappe.query_reports["General Ledger"] = {
|
||||
"label": __("Currency"),
|
||||
"fieldtype": "Select",
|
||||
"options": erpnext.get_presentation_currency_list()
|
||||
},
|
||||
{
|
||||
"fieldname": "show_opening_entries",
|
||||
"label": __("Show Opening Entries"),
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -283,7 +283,8 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
|
||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||
for gle in gl_entries:
|
||||
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
|
||||
if (gle.posting_date < from_date or
|
||||
(cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))):
|
||||
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
|
||||
update_value_in_dict(totals, 'opening', gle)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ def get_columns():
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
"fieldname": "item",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"label": "Item",
|
||||
@@ -82,12 +82,12 @@ def get_data(filters):
|
||||
row = {
|
||||
"territory": territory.name,
|
||||
"item_group": item.item_group,
|
||||
"item": item.name,
|
||||
"item": item.item_code,
|
||||
"item_name": item.item_name
|
||||
}
|
||||
|
||||
if sales_invoice_data.get((territory.name,item.name)):
|
||||
item_obj = sales_invoice_data[(territory.name,item.name)]
|
||||
if sales_invoice_data.get((territory.name,item.item_code)):
|
||||
item_obj = sales_invoice_data[(territory.name,item.item_code)]
|
||||
if item_obj.days_since_last_order > cint(filters['days']):
|
||||
row.update({
|
||||
"territory": item_obj.territory,
|
||||
@@ -111,15 +111,15 @@ def get_sales_details(filters):
|
||||
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
|
||||
|
||||
sales_data = frappe.db.sql("""
|
||||
select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date,
|
||||
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
|
||||
DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
|
||||
from `tab{doctype}` s, `tab{doctype} Item` si
|
||||
where s.name = si.parent and s.docstatus = 1
|
||||
group by si.name order by days_since_last_order """ #nosec
|
||||
order by days_since_last_order """ #nosec
|
||||
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
||||
|
||||
for d in sales_data:
|
||||
item_details_map.setdefault((d.territory,d.item_name), d)
|
||||
item_details_map.setdefault((d.territory,d.item_code), d)
|
||||
|
||||
return item_details_map
|
||||
|
||||
@@ -149,6 +149,6 @@ def get_items(filters):
|
||||
"name": filters["item"]
|
||||
})
|
||||
|
||||
items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name")
|
||||
items = frappe.get_all("Item", fields=["name", "item_group", "item_name", "item_code"], filters=filters_dict, order_by="name")
|
||||
|
||||
return items
|
||||
|
||||
@@ -333,6 +333,9 @@ def reconcile_against_document(args):
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||
|
||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||
doc.update_expense_claim()
|
||||
|
||||
def check_if_advance_entry_modified(args):
|
||||
"""
|
||||
check if there is already a voucher reference
|
||||
@@ -375,9 +378,9 @@ def check_if_advance_entry_modified(args):
|
||||
|
||||
def validate_allocated_amount(args):
|
||||
if args.get("allocated_amount") < 0:
|
||||
throw(_("Allocated amount can not be negative"))
|
||||
throw(_("Allocated amount cannot be negative"))
|
||||
elif args.get("allocated_amount") > args.get("unadjusted_amount"):
|
||||
throw(_("Allocated amount can not greater than unadjusted amount"))
|
||||
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
||||
|
||||
def update_reference_in_journal_entry(d, jv_obj):
|
||||
"""
|
||||
|
||||
@@ -8,7 +8,9 @@ import frappe.defaults
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from frappe.utils import flt, add_days, nowdate, getdate
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order \
|
||||
import (make_purchase_receipt, make_purchase_invoice as make_pi_from_po, make_rm_stock_entry as make_subcontract_transfer_entry)
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_pi_from_pr
|
||||
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
@@ -62,7 +64,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.update_stock = 1
|
||||
pi.items[0].qty = 12
|
||||
pi.insert()
|
||||
@@ -89,7 +91,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
create_pr_against_po(po.name)
|
||||
|
||||
make_purchase_invoice(po.name)
|
||||
make_pi_from_po(po.name)
|
||||
|
||||
existing_ordered_qty = get_ordered_qty()
|
||||
existing_requested_qty = get_requested_qty()
|
||||
@@ -108,14 +110,85 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 2)
|
||||
|
||||
# Check received_qty after making PI from PR without update_stock checked
|
||||
pi1 = make_pi_from_pr(pr.name)
|
||||
pi1.get("items")[0].qty = 2
|
||||
pi1.insert()
|
||||
pi1.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 2)
|
||||
|
||||
# Check received_qty after making PI from PO with update_stock checked
|
||||
pi2 = make_pi_from_po(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||
|
||||
# Check received_qty after making PR from PO
|
||||
pr = make_pr_against_po(po.name, 1)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
|
||||
|
||||
def test_return_against_purchase_order(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
pr = make_pr_against_po(po.name, 6)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
pi2 = make_pi_from_po(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||
|
||||
# Make return purchase receipt, purchase invoice and check quantity
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import make_purchase_receipt as make_purchase_receipt_return
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||
import make_purchase_invoice as make_purchase_invoice_return
|
||||
|
||||
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
|
||||
pr1.items[0].purchase_order = po.name
|
||||
pr1.items[0].purchase_order_item = po.items[0].name
|
||||
pr1.submit()
|
||||
|
||||
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||
pi1.items[0].purchase_order = po.name
|
||||
pi1.items[0].po_detail = po.items[0].name
|
||||
pi1.submit()
|
||||
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||
|
||||
def test_make_purchase_invoice(self):
|
||||
po = create_purchase_order(do_not_submit=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
|
||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||
|
||||
po.submit()
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
|
||||
self.assertEqual(pi.doctype, "Purchase Invoice")
|
||||
self.assertEqual(len(pi.get("items", [])), 1)
|
||||
@@ -123,7 +196,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
def test_make_purchase_invoice_with_terms(self):
|
||||
po = create_purchase_order(do_not_save=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
|
||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||
|
||||
po.update(
|
||||
{"payment_terms_template": "_Test Payment Term Template"}
|
||||
@@ -136,7 +209,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.save()
|
||||
|
||||
self.assertEqual(pi.doctype, "Purchase Invoice")
|
||||
@@ -274,7 +347,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
|
||||
self.assertFalse(pi.get('payment_schedule'))
|
||||
|
||||
@@ -285,7 +358,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.submit()
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.insert()
|
||||
self.assertTrue(pi.get('payment_schedule'))
|
||||
|
||||
@@ -365,7 +438,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
|
||||
|
||||
# Make Purchase Invoice
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.update_stock = 1
|
||||
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
||||
pi.insert()
|
||||
@@ -469,6 +542,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(se_items, supplied_items)
|
||||
update_backflush_based_on("BOM")
|
||||
|
||||
def make_pr_against_po(po, received_qty=0):
|
||||
pr = make_purchase_receipt(po)
|
||||
pr.get("items")[0].qty = received_qty or 5
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
return pr
|
||||
|
||||
def make_subcontracted_item(item_code):
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
|
||||
|
||||
@@ -237,6 +237,10 @@ class AccountsController(TransactionBase):
|
||||
document_type = "{} Item".format(self.doctype)
|
||||
parent_dict.update({"document_type": document_type})
|
||||
|
||||
# party_name field used for customer in quotation
|
||||
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
|
||||
parent_dict.update({"customer": parent_dict.get("party_name")})
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get("item_code"):
|
||||
args = parent_dict.copy()
|
||||
@@ -344,7 +348,7 @@ class AccountsController(TransactionBase):
|
||||
'fiscal_year': fiscal_year,
|
||||
'voucher_type': self.doctype,
|
||||
'voucher_no': self.name,
|
||||
'remarks': self.get("remarks"),
|
||||
'remarks': self.get("remarks") or self.get("remark"),
|
||||
'debit': 0,
|
||||
'credit': 0,
|
||||
'debit_in_account_currency': 0,
|
||||
|
||||
@@ -287,14 +287,14 @@ def copy_attributes_to_variant(item, variant):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
|
||||
variant.variant_of = item.name
|
||||
if 'description' in allow_fields:
|
||||
variant.has_variants = 0
|
||||
|
||||
if 'description' not in allow_fields:
|
||||
if not variant.description:
|
||||
variant.description = ""
|
||||
variant.description = ""
|
||||
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
attributes_description = ""
|
||||
attributes_description = item.description + " "
|
||||
for d in variant.attributes:
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ status_map = {
|
||||
],
|
||||
"Sales Order": [
|
||||
["Draft", None],
|
||||
["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1 and self.order_type in ['Sales', 'Shopping Cart']"],
|
||||
["To Bill", "eval:self.per_delivered == 100 or self.order_type == 'Maintenance' and self.per_billed < 100 and self.docstatus == 1"],
|
||||
["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1"],
|
||||
["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
|
||||
["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
|
||||
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
|
||||
@@ -45,9 +45,9 @@ status_map = {
|
||||
"Sales Invoice": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
|
||||
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
||||
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
|
||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
@@ -55,9 +55,9 @@ status_map = {
|
||||
"Purchase Invoice": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
|
||||
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
||||
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
|
||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
@@ -316,19 +316,27 @@ class StatusUpdater(Document):
|
||||
.format(frappe.db.escape(frappe.session.user))
|
||||
|
||||
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
||||
ref_fieldname = ref_dt.lower().replace(" ", "_")
|
||||
zero_amount_refdoc = []
|
||||
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
|
||||
where docstatus=1 and base_net_total = 0""" % ref_dt)
|
||||
ref_fieldname = frappe.scrub(ref_dt)
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get(ref_fieldname) \
|
||||
and item.get(ref_fieldname) in all_zero_amount_refdoc \
|
||||
and item.get(ref_fieldname) not in zero_amount_refdoc:
|
||||
zero_amount_refdoc.append(item.get(ref_fieldname))
|
||||
ref_docs = [item.get(ref_fieldname) for item in (self.get('items') or []) if item.get(ref_fieldname)]
|
||||
if not ref_docs:
|
||||
return
|
||||
|
||||
if zero_amount_refdoc:
|
||||
self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
||||
zero_amount_refdocs = frappe.db.sql_list("""
|
||||
SELECT
|
||||
name
|
||||
from
|
||||
`tab{ref_dt}`
|
||||
where
|
||||
docstatus = 1
|
||||
and base_net_total = 0
|
||||
and name in %(ref_docs)s
|
||||
""".format(ref_dt=ref_dt), {
|
||||
'ref_docs': ref_docs
|
||||
})
|
||||
|
||||
if zero_amount_refdocs:
|
||||
self.update_billing_status(zero_amount_refdocs, ref_dt, ref_fieldname)
|
||||
|
||||
def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
||||
for ref_dn in zero_amount_refdoc:
|
||||
|
||||
@@ -80,6 +80,7 @@ class StockController(AccountsController):
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": flt(sle.stock_value_difference, 2),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||
}, warehouse_account[sle.warehouse]["account_currency"]))
|
||||
|
||||
# to target warehouse / expense account
|
||||
@@ -89,7 +90,8 @@ class StockController(AccountsController):
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": flt(sle.stock_value_difference, 2),
|
||||
"project": item_row.get("project") or self.get("project")
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
|
||||
}))
|
||||
elif sle.warehouse not in warehouse_with_no_account:
|
||||
warehouse_with_no_account.append(sle.warehouse)
|
||||
@@ -123,8 +125,17 @@ class StockController(AccountsController):
|
||||
|
||||
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
|
||||
if self.doctype == "Stock Reconciliation":
|
||||
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
|
||||
"cost_center": default_cost_center }) for voucher_detail_no, sle in sle_map.items()]
|
||||
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
|
||||
is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No"
|
||||
details = []
|
||||
for voucher_detail_no in sle_map:
|
||||
details.append(frappe._dict({
|
||||
"name": voucher_detail_no,
|
||||
"expense_account": default_expense_account,
|
||||
"cost_center": default_cost_center,
|
||||
"is_opening": is_opening
|
||||
}))
|
||||
return details
|
||||
else:
|
||||
details = self.get("items")
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ class Lead(SellingController):
|
||||
def set_lead_name(self):
|
||||
if not self.lead_name:
|
||||
# Check for leads being created through data import
|
||||
if not self.company_name:
|
||||
if not self.company_name and not self.flags.ignore_mandatory:
|
||||
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
||||
|
||||
self.lead_name = self.company_name
|
||||
@@ -150,8 +150,8 @@ def make_opportunity(source_name, target_doc=None):
|
||||
"doctype": "Opportunity",
|
||||
"field_map": {
|
||||
"campaign_name": "campaign",
|
||||
"doctype": "enquiry_from",
|
||||
"name": "lead",
|
||||
"doctype": "opportunity_from",
|
||||
"name": "party_name",
|
||||
"lead_name": "contact_display",
|
||||
"company_name": "customer_name",
|
||||
"email_id": "contact_email",
|
||||
|
||||
@@ -20,10 +20,22 @@ frappe.ui.form.on("Opportunity", {
|
||||
});
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
party_name: function(frm) {
|
||||
frm.toggle_display("contact_info", frm.doc.party_name);
|
||||
|
||||
if (frm.doc.opportunity_from == "Customer") {
|
||||
frm.trigger('set_contact_link');
|
||||
erpnext.utils.get_party_details(frm);
|
||||
} else if (frm.doc.opportunity_from == "Lead") {
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
source_name: frm.doc.party_name,
|
||||
frm: frm
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -82,9 +94,9 @@ frappe.ui.form.on("Opportunity", {
|
||||
|
||||
set_contact_link: function(frm) {
|
||||
if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Customer'}
|
||||
} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'}
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Lead'}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -138,12 +150,14 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
};
|
||||
});
|
||||
|
||||
$.each([["lead", "lead"],
|
||||
["customer", "customer"],
|
||||
["contact_person", "contact_query"]],
|
||||
function(i, opts) {
|
||||
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
|
||||
});
|
||||
me.frm.set_query('contact_person', erpnext.queries['contact_query'])
|
||||
|
||||
if (me.frm.doc.opportunity_from == "Lead") {
|
||||
me.frm.set_query('party_name', erpnext.queries['lead']);
|
||||
}
|
||||
else if (me.frm.doc.opportunity_from == "Cuatomer") {
|
||||
me.frm.set_query('party_name', erpnext.queries['customer']);
|
||||
}
|
||||
},
|
||||
|
||||
create_quotation: function() {
|
||||
@@ -156,11 +170,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
||||
if(doc.opportunity_from == 'Lead' && doc.party_name)
|
||||
cur_frm.cscript.lead(doc, cdt, cdn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.item_code) {
|
||||
@@ -179,15 +188,6 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.lead = function(doc, cdt, cdn) {
|
||||
cur_frm.toggle_display("contact_info", doc.party_name);
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
source_name: cur_frm.doc.party_name,
|
||||
frm: cur_frm
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Declare Opportunity Lost'] = function() {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Set as Lost"),
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Opportunity From",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@@ -878,7 +878,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "next_contact_by",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_info",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -912,7 +912,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.customer || doc.lead",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "customer_address",
|
||||
"fieldtype": "Link",
|
||||
@@ -1083,7 +1083,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_person",
|
||||
"fieldtype": "Link",
|
||||
@@ -1150,7 +1150,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Data",
|
||||
@@ -1183,7 +1183,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Small Text",
|
||||
@@ -1468,7 +1468,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-05-11 19:22:33.533487",
|
||||
"modified": "2019-05-17 19:03:32.740910",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Opportunity",
|
||||
|
||||
@@ -11,7 +11,7 @@ def execute(filters=None):
|
||||
columns=get_columns("Campaign Name")
|
||||
data=get_lead_data(filters or {}, "Campaign Name")
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns(based_on):
|
||||
return [
|
||||
{
|
||||
@@ -69,21 +69,21 @@ def get_columns(based_on):
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def get_lead_data(filters, based_on):
|
||||
based_on_field = frappe.scrub(based_on)
|
||||
conditions = get_filter_conditions(filters)
|
||||
|
||||
|
||||
lead_details = frappe.db.sql("""
|
||||
select {based_on_field}, name
|
||||
from `tabLead`
|
||||
where {based_on_field} is not null and {based_on_field} != '' {conditions}
|
||||
from `tabLead`
|
||||
where {based_on_field} is not null and {based_on_field} != '' {conditions}
|
||||
""".format(based_on_field=based_on_field, conditions=conditions), filters, as_dict=1)
|
||||
|
||||
|
||||
lead_map = frappe._dict()
|
||||
for d in lead_details:
|
||||
lead_map.setdefault(d.get(based_on_field), []).append(d.name)
|
||||
|
||||
|
||||
data = []
|
||||
for based_on_value, leads in lead_map.items():
|
||||
row = {
|
||||
@@ -94,42 +94,42 @@ def get_lead_data(filters, based_on):
|
||||
row["opp_count"] = get_lead_opp_count(leads)
|
||||
row["order_count"] = get_quotation_ordered_count(leads)
|
||||
row["order_value"] = get_order_amount(leads) or 0
|
||||
|
||||
|
||||
row["opp_lead"] = flt(row["opp_count"]) / flt(row["lead_count"] or 1.0) * 100.0
|
||||
row["quot_lead"] = flt(row["quot_count"]) / flt(row["lead_count"] or 1.0) * 100.0
|
||||
|
||||
|
||||
row["order_quot"] = flt(row["order_count"]) / flt(row["quot_count"] or 1.0) * 100.0
|
||||
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_filter_conditions(filters):
|
||||
conditions=""
|
||||
if filters.from_date:
|
||||
conditions += " and date(creation) >= %(from_date)s"
|
||||
if filters.to_date:
|
||||
conditions += " and date(creation) <= %(to_date)s"
|
||||
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_lead_quotation_count(leads):
|
||||
return frappe.db.sql("""select count(name) from `tabQuotation`
|
||||
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name) from `tabQuotation`
|
||||
where quotation_to = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] #nosec
|
||||
|
||||
def get_lead_opp_count(leads):
|
||||
return frappe.db.sql("""select count(name) from `tabOpportunity`
|
||||
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name) from `tabOpportunity`
|
||||
where opportunity_from = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
def get_quotation_ordered_count(leads):
|
||||
return frappe.db.sql("""select count(name)
|
||||
from `tabQuotation` where status = 'Ordered'
|
||||
and lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name)
|
||||
from `tabQuotation` where status = 'Ordered' and quotation_to = 'Lead'
|
||||
and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
def get_order_amount(leads):
|
||||
return frappe.db.sql("""select sum(base_net_amount)
|
||||
return frappe.db.sql("""select sum(base_net_amount)
|
||||
from `tabSales Order Item`
|
||||
where prevdoc_docname in (
|
||||
select name from `tabQuotation` where status = 'Ordered'
|
||||
and lead in (%s)
|
||||
select name from `tabQuotation` where status = 'Ordered'
|
||||
and quotation_to = 'Lead' and party_name in (%s)
|
||||
)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
@@ -11,16 +11,61 @@ def execute(filters=None):
|
||||
columns=get_columns()
|
||||
data=get_lead_data(filters, "Lead Owner")
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Lead Owner") + ":Data:130",
|
||||
_("Lead Count") + ":Int:80",
|
||||
_("Opp Count") + ":Int:80",
|
||||
_("Quot Count") + ":Int:80",
|
||||
_("Order Count") + ":Int:100",
|
||||
_("Order Value") + ":Float:100",
|
||||
_("Opp/Lead %") + ":Float:100",
|
||||
_("Quot/Lead %") + ":Float:100",
|
||||
_("Order/Quot %") + ":Float:100"
|
||||
{
|
||||
"fieldname": "lead_owner",
|
||||
"label": _("Lead Owner"),
|
||||
"fieldtype": "Data",
|
||||
"width": "130"
|
||||
},
|
||||
{
|
||||
"fieldname": "lead_count",
|
||||
"label": _("Lead Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_count",
|
||||
"label": _("Opp Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_count",
|
||||
"label": _("Quot Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_count",
|
||||
"label": _("Order Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_value",
|
||||
"label": _("Order Value"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_lead",
|
||||
"label": _("Opp/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_lead",
|
||||
"label": _("Quot/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_quot",
|
||||
"label": _("Order/Quot %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
}
|
||||
]
|
||||
@@ -35,14 +35,14 @@ def get_data(filters):
|
||||
|
||||
for lead in frappe.get_all('Lead', fields = ['name', 'lead_name', 'company_name'], filters=lead_filters):
|
||||
data = frappe.db.sql("""
|
||||
select
|
||||
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
|
||||
select
|
||||
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
|
||||
`tabCommunication`.content, `tabCommunication`.communication_date
|
||||
from
|
||||
from
|
||||
(
|
||||
(select name, lead from `tabOpportunity` where lead = %(lead)s)
|
||||
union
|
||||
(select name, lead from `tabQuotation` where lead = %(lead)s)
|
||||
(select name, party_name as lead from `tabOpportunity` where opportunity_from='Lead' and party_name = %(lead)s)
|
||||
union
|
||||
(select name, party_name as lead from `tabQuotation` where quotation_to = 'Lead' and party_name = %(lead)s)
|
||||
union
|
||||
(select name, lead from `tabIssue` where lead = %(lead)s and status!='Closed')
|
||||
union
|
||||
|
||||
@@ -107,10 +107,18 @@ def get_series():
|
||||
|
||||
def setup_custom_fields():
|
||||
custom_fields = {
|
||||
"Customer": [dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
|
||||
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)],
|
||||
"Address": [dict(fieldname='shopify_address_id', label='Shopify Address Id',
|
||||
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)],
|
||||
"Customer": [
|
||||
dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
|
||||
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)
|
||||
],
|
||||
"Supplier": [
|
||||
dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id',
|
||||
fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1)
|
||||
],
|
||||
"Address": [
|
||||
dict(fieldname='shopify_address_id', label='Shopify Address Id',
|
||||
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)
|
||||
],
|
||||
"Item": [
|
||||
dict(fieldname='shopify_variant_id', label='Shopify Variant Id',
|
||||
fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
|
||||
@@ -119,16 +127,20 @@ def setup_custom_fields():
|
||||
dict(fieldname='shopify_description', label='Shopify Description',
|
||||
fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1)
|
||||
],
|
||||
"Sales Order": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)],
|
||||
"Sales Order": [
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
],
|
||||
"Delivery Note":[
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
|
||||
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
],
|
||||
"Sales Invoice": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)]
|
||||
"Sales Invoice": [
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
]
|
||||
}
|
||||
|
||||
create_custom_fields(custom_fields)
|
||||
|
||||
@@ -82,7 +82,7 @@ def get_healthcare_services_to_invoice(patient):
|
||||
'service': service_item, 'rate': practitioner_charge,
|
||||
'income_account': income_account})
|
||||
|
||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False})
|
||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
|
||||
if lab_tests:
|
||||
for lab_test in lab_tests:
|
||||
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
||||
|
||||
@@ -9,6 +9,12 @@ from frappe import _
|
||||
from frappe.utils import getdate, date_diff
|
||||
|
||||
class AdditionalSalary(Document):
|
||||
def before_insert(self):
|
||||
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
||||
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company}):
|
||||
|
||||
frappe.throw(_("Additional Salary Component Exists."))
|
||||
|
||||
def validate(self):
|
||||
self.validate_dates()
|
||||
if self.amount < 0:
|
||||
|
||||
@@ -219,7 +219,8 @@ frappe.ui.form.on("Expense Claim", {
|
||||
frm.fields_dict["cost_center"].get_query = function() {
|
||||
return {
|
||||
filters: {
|
||||
"company": frm.doc.company
|
||||
"company": frm.doc.company,
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -230,7 +231,9 @@ frappe.ui.form.on("Expense Claim", {
|
||||
return {
|
||||
filters: {
|
||||
"report_type": "Balance Sheet",
|
||||
"account_type": "Payable"
|
||||
"account_type": "Payable",
|
||||
"company": frm.doc.company,
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
20
erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
Normal file
20
erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'reference_name',
|
||||
'internal_links': {
|
||||
'Employee Advance': ['advances', 'employee_advance']
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Payment'),
|
||||
'items': ['Payment Entry', 'Journal Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Reference'),
|
||||
'items': ['Employee Advance']
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -79,6 +79,7 @@ def get_events(start, end, filters=None):
|
||||
filters.append(['Holiday', 'holiday_date', '>', getdate(start)])
|
||||
if end:
|
||||
filters.append(['Holiday', 'holiday_date', '<', getdate(end)])
|
||||
|
||||
return frappe.get_list('Holiday List',
|
||||
fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'],
|
||||
filters = filters,
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
frappe.views.calendar["Holiday List"] = {
|
||||
field_map: {
|
||||
"start": "from_date",
|
||||
"end": "to_date",
|
||||
"start": "holiday_date",
|
||||
"end": "holiday_date",
|
||||
"id": "name",
|
||||
"title": "description",
|
||||
"allDay": "allDay"
|
||||
|
||||
@@ -146,7 +146,7 @@ frappe.ui.form.on('Loan', {
|
||||
'payment_date': payment.payment_date,
|
||||
'principal_amount': payment.principal_amount,
|
||||
'interest_amount': payment.interest_amount,
|
||||
'total_payment': payment.total_payment
|
||||
'total_payment': payment.total_payment
|
||||
});
|
||||
dialog.fields_dict.payments.grid.refresh();
|
||||
$(dialog.wrapper.find(".grid-buttons")).hide();
|
||||
@@ -172,18 +172,20 @@ frappe.ui.form.on('Loan', {
|
||||
},
|
||||
|
||||
mode_of_payment: function (frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||
args: {
|
||||
"mode_of_payment": frm.doc.mode_of_payment,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
callback: function (r, rt) {
|
||||
if (r.message) {
|
||||
frm.set_value("payment_account", r.message.account);
|
||||
if (frm.doc.mode_of_payment && frm.doc.company) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||
args: {
|
||||
"mode_of_payment": frm.doc.mode_of_payment,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
callback: function (r, rt) {
|
||||
if (r.message) {
|
||||
frm.set_value("payment_account", r.message.account);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loan_application: function (frm) {
|
||||
|
||||
@@ -119,7 +119,7 @@ class SalarySlip(TransactionBase):
|
||||
if not self.salary_slip_based_on_timesheet:
|
||||
self.get_date_details()
|
||||
self.validate_dates()
|
||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||
["date_of_joining", "relieving_date"])
|
||||
|
||||
self.get_leave_details(joining_date, relieving_date)
|
||||
@@ -183,7 +183,7 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
|
||||
if not joining_date:
|
||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||
["date_of_joining", "relieving_date"])
|
||||
|
||||
working_days = date_diff(self.end_date, self.start_date) + 1
|
||||
@@ -297,9 +297,6 @@ class SalarySlip(TransactionBase):
|
||||
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
|
||||
self.rounded_total = rounded(self.net_pay)
|
||||
|
||||
if self.net_pay < 0:
|
||||
frappe.throw(_("Net Pay cannnot be negative"))
|
||||
|
||||
def calculate_component_amounts(self):
|
||||
if not getattr(self, '_salary_structure_doc', None):
|
||||
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
||||
@@ -310,6 +307,7 @@ class SalarySlip(TransactionBase):
|
||||
self.add_employee_benefits(payroll_period)
|
||||
self.add_additional_salary_components()
|
||||
self.add_tax_components(payroll_period)
|
||||
self.set_component_amounts_based_on_payment_days()
|
||||
|
||||
def add_structure_components(self):
|
||||
data = self.get_data_for_eval()
|
||||
@@ -401,14 +399,18 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
def add_tax_components(self, payroll_period):
|
||||
# Calculate variable_based_on_taxable_salary after all components updated in salary slip
|
||||
struct_tax_components = [d.salary_component for d in self._salary_structure_doc.get("deductions")
|
||||
if d.variable_based_on_taxable_salary == 1 and not d.formula and not d.amount]
|
||||
tax_components, other_deduction_components = [], []
|
||||
for d in self._salary_structure_doc.get("deductions"):
|
||||
if d.variable_based_on_taxable_salary == 1 and not d.formula and not flt(d.amount):
|
||||
tax_components.append(d.salary_component)
|
||||
else:
|
||||
other_deduction_components.append(d.salary_component)
|
||||
|
||||
if not struct_tax_components:
|
||||
struct_tax_components = [d.name for d in
|
||||
frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})]
|
||||
if not tax_components:
|
||||
tax_components = [d.name for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})
|
||||
if d.name not in other_deduction_components]
|
||||
|
||||
for d in struct_tax_components:
|
||||
for d in tax_components:
|
||||
tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
|
||||
tax_row = self.get_salary_slip_row(d)
|
||||
self.update_component_row(tax_row, tax_amount, "deductions")
|
||||
@@ -474,8 +476,7 @@ class SalarySlip(TransactionBase):
|
||||
future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1)
|
||||
|
||||
# get taxable_earnings, addition_earnings for current actual payment days
|
||||
self.set_component_amounts_based_on_payment_days()
|
||||
current_taxable_earnings_for_payment_days = self.get_taxable_earnings()
|
||||
current_taxable_earnings_for_payment_days = self.get_taxable_earnings(based_on_payment_days=1)
|
||||
current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings
|
||||
current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income
|
||||
current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax
|
||||
@@ -498,7 +499,6 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
# Structured tax amount
|
||||
total_structured_tax_amount = self.calculate_tax_by_tax_slab(payroll_period, total_taxable_earnings_without_full_tax_addl_components)
|
||||
|
||||
current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
|
||||
|
||||
# Total taxable earnings with additional earnings with full tax
|
||||
@@ -557,25 +557,39 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
return total_tax_paid
|
||||
|
||||
def get_taxable_earnings(self, only_flexi=0):
|
||||
def get_taxable_earnings(self, based_on_payment_days=0):
|
||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||
["date_of_joining", "relieving_date"])
|
||||
|
||||
if not relieving_date:
|
||||
relieving_date = getdate(self.end_date)
|
||||
|
||||
if not joining_date:
|
||||
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
|
||||
|
||||
taxable_earnings = 0
|
||||
additional_income = 0
|
||||
additional_income_with_full_tax = 0
|
||||
flexi_benefits = 0
|
||||
|
||||
for earning in self.earnings:
|
||||
if based_on_payment_days:
|
||||
amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
|
||||
else:
|
||||
amount, additional_amount = earning.amount, earning.additional_amount
|
||||
|
||||
if earning.is_tax_applicable:
|
||||
if flt(earning.additional_amount):
|
||||
taxable_earnings += (earning.amount - earning.additional_amount)
|
||||
additional_income += earning.additional_amount
|
||||
if additional_amount:
|
||||
taxable_earnings += (amount - additional_amount)
|
||||
additional_income += additional_amount
|
||||
if earning.deduct_full_tax_on_selected_payroll_date:
|
||||
additional_income_with_full_tax += earning.additional_amount
|
||||
additional_income_with_full_tax += additional_amount
|
||||
continue
|
||||
|
||||
if earning.is_flexible_benefit:
|
||||
flexi_benefits += earning.amount
|
||||
flexi_benefits += amount
|
||||
else:
|
||||
taxable_earnings += earning.amount
|
||||
taxable_earnings += amount
|
||||
|
||||
return frappe._dict({
|
||||
"taxable_earnings": taxable_earnings,
|
||||
@@ -584,6 +598,26 @@ class SalarySlip(TransactionBase):
|
||||
"flexi_benefits": flexi_benefits
|
||||
})
|
||||
|
||||
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
|
||||
amount, additional_amount = row.amount, row.additional_amount
|
||||
if (self.salary_structure and
|
||||
cint(row.depends_on_payment_days) and cint(self.total_working_days) and
|
||||
(not self.salary_slip_based_on_timesheet or
|
||||
getdate(self.start_date) < joining_date or
|
||||
getdate(self.end_date) > relieving_date
|
||||
)):
|
||||
additional_amount = flt((flt(row.additional_amount) * flt(self.payment_days)
|
||||
/ cint(self.total_working_days)), row.precision("additional_amount"))
|
||||
amount = flt((flt(row.default_amount) * flt(self.payment_days)
|
||||
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
|
||||
|
||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
||||
amount, additional_amount = 0, 0
|
||||
elif not row.amount:
|
||||
amount = row.default_amount + row.additional_amount
|
||||
|
||||
return amount, additional_amount
|
||||
|
||||
def calculate_unclaimed_taxable_benefits(self, payroll_period):
|
||||
# get total sum of benefits paid
|
||||
total_benefits_paid = flt(frappe.db.sql("""
|
||||
@@ -685,7 +719,7 @@ class SalarySlip(TransactionBase):
|
||||
return total
|
||||
|
||||
def set_component_amounts_based_on_payment_days(self):
|
||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||
["date_of_joining", "relieving_date"])
|
||||
|
||||
if not relieving_date:
|
||||
@@ -696,22 +730,7 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
for component_type in ("earnings", "deductions"):
|
||||
for d in self.get(component_type):
|
||||
if (self.salary_structure and
|
||||
cint(d.depends_on_payment_days) and cint(self.total_working_days) and
|
||||
(not self.salary_slip_based_on_timesheet or
|
||||
getdate(self.start_date) < joining_date or
|
||||
getdate(self.end_date) > relieving_date
|
||||
)):
|
||||
|
||||
d.amount = flt(
|
||||
(flt(d.default_amount + d.additional_amount) * flt(self.payment_days)
|
||||
/ cint(self.total_working_days))
|
||||
, d.precision("amount"))
|
||||
|
||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(d.depends_on_payment_days):
|
||||
d.amount = 0
|
||||
elif not d.amount:
|
||||
d.amount = d.default_amount + d.additional_amount
|
||||
d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
|
||||
|
||||
def set_loan_repayment(self):
|
||||
self.set('loans', [])
|
||||
|
||||
@@ -442,7 +442,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
"formula": 'base*.1',
|
||||
"type": "Deduction",
|
||||
"amount_based_on_formula": 1,
|
||||
"depends_on_payment_days": 0
|
||||
"depends_on_payment_days": 0,
|
||||
"variable_based_on_taxable_salary": 1
|
||||
}
|
||||
]
|
||||
if not test_tax:
|
||||
|
||||
@@ -15,11 +15,11 @@ class TrainingFeedback(Document):
|
||||
|
||||
def on_submit(self):
|
||||
training_event = frappe.get_doc("Training Event", self.training_event)
|
||||
status = None
|
||||
event_status = None
|
||||
for e in training_event.employees:
|
||||
if e.employee == self.employee:
|
||||
status = 'Feedback Submitted'
|
||||
event_status = 'Feedback Submitted'
|
||||
break
|
||||
|
||||
if status:
|
||||
frappe.db.set_value("Training Event", self.training_event, "status", status)
|
||||
if event_status:
|
||||
frappe.db.set_value("Training Event", self.training_event, "event_status", event_status)
|
||||
|
||||
@@ -31,5 +31,9 @@ frappe.ui.form.on('Blanket Order', {
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -205,7 +205,12 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
|
||||
'item_code': d.item_code,
|
||||
'bom_no': d.bom_no != null ? d.bom_no: '',
|
||||
"scrap_items": scrap_items,
|
||||
'qty': d.qty
|
||||
'qty': d.qty,
|
||||
"stock_qty": d.stock_qty,
|
||||
"include_item_in_manufacturing": d.include_item_in_manufacturing,
|
||||
"uom": d.uom,
|
||||
"stock_uom": d.stock_uom,
|
||||
"conversion_factor": d.conversion_factor
|
||||
},
|
||||
callback: function(r) {
|
||||
d = locals[cdt][cdn];
|
||||
|
||||
@@ -170,13 +170,14 @@ class BOM(WebsiteGenerator):
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif arg:
|
||||
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
||||
rate = self.get_bom_unitcost(arg['bom_no'])
|
||||
rate = self.get_bom_unitcost(arg['bom_no']) * (arg.get("conversion_factor") or 1)
|
||||
else:
|
||||
if self.rm_cost_as_per == 'Valuation Rate':
|
||||
rate = self.get_valuation_rate(arg)
|
||||
rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1)
|
||||
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
||||
rate = arg.get('last_purchase_rate') \
|
||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
|
||||
rate = (arg.get('last_purchase_rate') \
|
||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \
|
||||
* (arg.get("conversion_factor") or 1)
|
||||
elif self.rm_cost_as_per == "Price List":
|
||||
if not self.buying_price_list:
|
||||
frappe.throw(_("Please select Price List"))
|
||||
@@ -189,7 +190,7 @@ class BOM(WebsiteGenerator):
|
||||
"transaction_type": "buying",
|
||||
"company": self.company,
|
||||
"currency": self.currency,
|
||||
"conversion_rate": self.conversion_rate or 1,
|
||||
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
|
||||
"conversion_factor": arg.get("conversion_factor") or 1,
|
||||
"plc_conversion_rate": 1,
|
||||
"ignore_party": True
|
||||
@@ -207,7 +208,7 @@ class BOM(WebsiteGenerator):
|
||||
frappe.msgprint(_("{0} not found for item {1}")
|
||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||
|
||||
return flt(rate)
|
||||
return flt(rate) / (self.conversion_rate or 1)
|
||||
|
||||
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
||||
if self.docstatus == 2:
|
||||
|
||||
@@ -9,6 +9,7 @@ from frappe.utils import cstr
|
||||
from frappe.test_runner import make_test_records
|
||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||
from six import string_types
|
||||
|
||||
test_records = frappe.get_test_records('BOM')
|
||||
|
||||
@@ -63,16 +64,8 @@ class TestBOM(unittest.TestCase):
|
||||
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
|
||||
rm_rate = rm_rate[0][0] if rm_rate else 0
|
||||
|
||||
# update valuation rate of item '_Test Item 2'
|
||||
warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin`
|
||||
where item_code='_Test Item 2' and actual_qty > 0""")
|
||||
|
||||
if not warehouse_list:
|
||||
warehouse_list.append("_Test Warehouse - _TC")
|
||||
|
||||
for warehouse in warehouse_list:
|
||||
create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse,
|
||||
qty=200, rate=rm_rate + 10)
|
||||
# Reset item valuation rate
|
||||
reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10)
|
||||
|
||||
# update cost of all BOMs based on latest valuation rate
|
||||
update_cost()
|
||||
@@ -96,7 +89,7 @@ class TestBOM(unittest.TestCase):
|
||||
self.assertEqual(bom.base_raw_material_cost, 480000)
|
||||
self.assertEqual(bom.base_total_cost, 486000)
|
||||
|
||||
def test_bom_cost_multi_uom_multi_currency(self):
|
||||
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
|
||||
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
|
||||
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
|
||||
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
|
||||
@@ -131,5 +124,35 @@ class TestBOM(unittest.TestCase):
|
||||
self.assertEqual(bom.base_raw_material_cost, 27000)
|
||||
self.assertEqual(bom.base_total_cost, 33000)
|
||||
|
||||
def test_bom_cost_multi_uom_based_on_valuation_rate(self):
|
||||
bom = frappe.copy_doc(test_records[2])
|
||||
bom.set_rate_of_sub_assembly_item_based_on_bom = 0
|
||||
bom.rm_cost_as_per = "Valuation Rate"
|
||||
bom.items[0].uom = "_Test UOM 1"
|
||||
bom.items[0].conversion_factor = 6
|
||||
bom.insert()
|
||||
|
||||
reset_item_valuation_rate(item_code='_Test Item', qty=200, rate=200)
|
||||
|
||||
bom.update_cost()
|
||||
|
||||
self.assertEqual(bom.items[0].rate, 20)
|
||||
|
||||
def get_default_bom(item_code="_Test FG Item 2"):
|
||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||
|
||||
def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
|
||||
if warehouse_list and isinstance(warehouse_list, string_types):
|
||||
warehouse_list = [warehouse_list]
|
||||
|
||||
if not warehouse_list:
|
||||
warehouse_list = frappe.db.sql_list("""
|
||||
select warehouse from `tabBin`
|
||||
where item_code=%s and actual_qty > 0
|
||||
""", item_code)
|
||||
|
||||
if not warehouse_list:
|
||||
warehouse_list.append("_Test Warehouse - _TC")
|
||||
|
||||
for warehouse in warehouse_list:
|
||||
create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)
|
||||
|
||||
@@ -18,21 +18,22 @@ class JobCard(Document):
|
||||
self.total_completed_qty = 0.0
|
||||
self.total_time_in_mins = 0.0
|
||||
|
||||
for d in self.get('time_logs'):
|
||||
if get_datetime(d.from_time) > get_datetime(d.to_time):
|
||||
frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx))
|
||||
if self.get('time_logs'):
|
||||
for d in self.get('time_logs'):
|
||||
if get_datetime(d.from_time) > get_datetime(d.to_time):
|
||||
frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx))
|
||||
|
||||
data = self.get_overlap_for(d)
|
||||
if data:
|
||||
frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
|
||||
.format(d.idx, self.name, data.name))
|
||||
data = self.get_overlap_for(d)
|
||||
if data:
|
||||
frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}")
|
||||
.format(d.idx, self.name, data.name))
|
||||
|
||||
if d.from_time and d.to_time:
|
||||
d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
|
||||
self.total_time_in_mins += d.time_in_mins
|
||||
if d.from_time and d.to_time:
|
||||
d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60
|
||||
self.total_time_in_mins += d.time_in_mins
|
||||
|
||||
if d.completed_qty:
|
||||
self.total_completed_qty += d.completed_qty
|
||||
if d.completed_qty:
|
||||
self.total_completed_qty += d.completed_qty
|
||||
|
||||
def get_overlap_for(self, args):
|
||||
existing = frappe.db.sql("""select jc.name as name from
|
||||
@@ -112,8 +113,10 @@ class JobCard(Document):
|
||||
for_quantity += doc.total_completed_qty
|
||||
time_in_mins += doc.total_time_in_mins
|
||||
for time_log in doc.time_logs:
|
||||
from_time_list.append(time_log.from_time)
|
||||
to_time_list.append(time_log.to_time)
|
||||
if time_log.from_time:
|
||||
from_time_list.append(time_log.from_time)
|
||||
if time_log.to_time:
|
||||
to_time_list.append(time_log.to_time)
|
||||
|
||||
if for_quantity:
|
||||
wo = frappe.get_doc('Work Order', self.work_order)
|
||||
|
||||
@@ -599,4 +599,5 @@ erpnext.patches.v11_1.rename_depends_on_lwp
|
||||
erpnext.patches.v11_1.set_missing_title_for_quotation
|
||||
execute:frappe.delete_doc("Report", "Inactive Items")
|
||||
erpnext.patches.v11_1.delete_scheduling_tool
|
||||
erpnext.patches.v11_1.update_bank_transaction_status
|
||||
erpnext.patches.v11_1.update_bank_transaction_status
|
||||
erpnext.patches.v11_1.renamed_delayed_item_report
|
||||
10
erpnext/patches/v11_1/renamed_delayed_item_report.py
Normal file
10
erpnext/patches/v11_1/renamed_delayed_item_report.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for report in ["Delayed Order Item Summary", "Delayed Order Summary"]:
|
||||
if frappe.db.exists("Report", report):
|
||||
frappe.delete_doc("Report", report)
|
||||
@@ -64,7 +64,7 @@ class Project(Document):
|
||||
'name': ("not in", self.deleted_task_list)
|
||||
})
|
||||
|
||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc")
|
||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
|
||||
|
||||
def validate(self):
|
||||
self.validate_project_name()
|
||||
|
||||
@@ -260,6 +260,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||
this.setup_item_selector();
|
||||
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1120,6 +1121,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
return {
|
||||
"items": this._get_item_list(item),
|
||||
"customer": me.frm.doc.customer || me.frm.doc.party_name,
|
||||
"quotation_to": me.frm.doc.quotation_to,
|
||||
"customer_group": me.frm.doc.customer_group,
|
||||
"territory": me.frm.doc.territory,
|
||||
"supplier": me.frm.doc.supplier,
|
||||
|
||||
@@ -75,12 +75,12 @@ def prepare_invoice(invoice, progressive_number):
|
||||
invoice.tax_data = tax_data
|
||||
|
||||
#Check if stamp duty (Bollo) of 2 EUR exists.
|
||||
stamp_duty_charge_row = next((tax for tax in invoice.taxes if tax.charge_type == _("Actual") and tax.tax_amount == 2.0 ), None)
|
||||
stamp_duty_charge_row = next((tax for tax in invoice.taxes if tax.charge_type == "Actual" and tax.tax_amount == 2.0 ), None)
|
||||
if stamp_duty_charge_row:
|
||||
invoice.stamp_duty = stamp_duty_charge_row.tax_amount
|
||||
|
||||
for item in invoice.e_invoice_items:
|
||||
if item.tax_rate == 0.0 and item.tax_amount == 0.0:
|
||||
if item.tax_rate == 0.0 and item.tax_amount == 0.0 and tax_data.get("0.0"):
|
||||
item.tax_exemption_reason = tax_data["0.0"]["tax_exemption_reason"]
|
||||
|
||||
customer_po_data = {}
|
||||
@@ -222,7 +222,7 @@ def sales_invoice_validate(doc):
|
||||
#Validate customer details
|
||||
customer = frappe.get_doc("Customer", doc.customer)
|
||||
|
||||
if customer.customer_type == _("Individual"):
|
||||
if customer.customer_type == "Individual":
|
||||
doc.customer_fiscal_code = customer.fiscal_code
|
||||
if not doc.customer_fiscal_code:
|
||||
frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
|
||||
|
||||
@@ -80,7 +80,10 @@ def get_gl_entries(filters):
|
||||
jnl.cheque_no as JnlRef, jnl.posting_date as JnlPostDate, jnl.title as JnlTitle,
|
||||
pay.name as PayName, pay.posting_date as PayPostDate, pay.title as PayTitle,
|
||||
cus.customer_name, cus.name as cusName,
|
||||
sup.supplier_name, sup.name as supName
|
||||
sup.supplier_name, sup.name as supName,
|
||||
emp.employee_name, emp.name as empName,
|
||||
stu.title as student_name, stu.name as stuName,
|
||||
member_name, mem.name as memName
|
||||
|
||||
from `tabGL Entry` gl
|
||||
left join `tabSales Invoice` inv on gl.voucher_no = inv.name
|
||||
@@ -89,6 +92,9 @@ def get_gl_entries(filters):
|
||||
left join `tabPayment Entry` pay on gl.voucher_no = pay.name
|
||||
left join `tabCustomer` cus on gl.party = cus.name
|
||||
left join `tabSupplier` sup on gl.party = sup.name
|
||||
left join `tabEmployee` emp on gl.party = emp.name
|
||||
left join `tabStudent` stu on gl.party = stu.name
|
||||
left join `tabMember` mem on gl.party = mem.name
|
||||
where gl.company=%(company)s and gl.fiscal_year=%(fiscal_year)s
|
||||
{group_by_condition}
|
||||
order by GlPostDate, voucher_no"""\
|
||||
@@ -128,6 +134,18 @@ def get_result_as_list(data, filters):
|
||||
CompAuxNum = d.get("supName")
|
||||
CompAuxLib = d.get("supplier_name")
|
||||
|
||||
elif d.get("party_type") == "Employee":
|
||||
CompAuxNum = d.get("empName")
|
||||
CompAuxLib = d.get("employee_name")
|
||||
|
||||
elif d.get("party_type") == "Student":
|
||||
CompAuxNum = d.get("stuName")
|
||||
CompAuxLib = d.get("student_name")
|
||||
|
||||
elif d.get("party_type") == "Member":
|
||||
CompAuxNum = d.get("memName")
|
||||
CompAuxLib = d.get("member_name")
|
||||
|
||||
else:
|
||||
CompAuxNum = ""
|
||||
CompAuxLib = ""
|
||||
|
||||
@@ -61,7 +61,7 @@ class Gstr1Report(object):
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
invoice_details = self.invoices.get(inv)
|
||||
for rate, items in items_based_on_rate.items():
|
||||
row = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
|
||||
if self.filters.get("type_of_business") == "CDNR":
|
||||
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
|
||||
@@ -118,7 +118,7 @@ class Gstr1Report(object):
|
||||
for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items])
|
||||
row += [tax_rate or 0, taxable_value]
|
||||
|
||||
return row
|
||||
return row, taxable_value
|
||||
|
||||
def get_invoice_data(self):
|
||||
self.invoices = frappe._dict()
|
||||
@@ -149,7 +149,8 @@ class Gstr1Report(object):
|
||||
|
||||
if self.filters.get("type_of_business") == "B2B":
|
||||
conditions += """ and ifnull(invoice_type, '') != 'Export' and is_return != 1
|
||||
and customer in ('{0}')""".format("', '".join([frappe.db.escape(c.name) for c in customers]))
|
||||
and customer in ('{0}') and (customer_gstin IS NOT NULL OR customer_gstin NOT IN ('', 'NA'))""".\
|
||||
format("', '".join([frappe.db.escape(c.name) for c in customers]))
|
||||
|
||||
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
|
||||
b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit')
|
||||
|
||||
@@ -47,6 +47,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
erpnext.utils.get_party_details(this.frm, null, null, function() {
|
||||
me.apply_price_list();
|
||||
});
|
||||
|
||||
if(me.frm.doc.quotation_to=="Lead" && me.frm.doc.party_name) {
|
||||
me.frm.trigger("get_lead_details");
|
||||
}
|
||||
},
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super(doc, dt, dn);
|
||||
@@ -87,10 +91,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
this.frm.add_custom_button(__('Opportunity'),
|
||||
function() {
|
||||
var setters = {};
|
||||
if(me.frm.doc.customer) {
|
||||
setters.customer = me.frm.doc.customer || undefined;
|
||||
} else if (me.frm.doc.lead) {
|
||||
setters.lead = me.frm.doc.lead || undefined;
|
||||
if(me.frm.doc.quotation_to == "Customer" && me.frm.doc.party_name) {
|
||||
setters.customer = me.frm.doc.party_name || undefined;
|
||||
} else if (me.frm.doc.quotation_to == "Lead" && me.frm.doc.party_name) {
|
||||
setters.lead = me.frm.doc.party_name || undefined;
|
||||
}
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
||||
@@ -162,16 +166,16 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
}
|
||||
},
|
||||
|
||||
lead: function() {
|
||||
get_lead_details: function() {
|
||||
var me = this;
|
||||
if(!this.frm.doc.lead) {
|
||||
if(!this.frm.doc.quotation_to === "Lead") {
|
||||
return;
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.crm.doctype.lead.lead.get_lead_details",
|
||||
args: {
|
||||
'lead': this.frm.doc.lead,
|
||||
'lead': this.frm.doc.party_name,
|
||||
'posting_date': this.frm.doc.transaction_date,
|
||||
'company': this.frm.doc.company,
|
||||
},
|
||||
|
||||
@@ -77,10 +77,6 @@ frappe.ui.form.on("Sales Order", {
|
||||
if(!d.delivery_date) d.delivery_date = frm.doc.delivery_date;
|
||||
});
|
||||
refresh_field("items");
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,12 +140,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 6) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
|
||||
if(flt(doc.per_delivered, 6) < 100 && allow_delivery) {
|
||||
this.frm.add_custom_button(__('Delivery'),
|
||||
function() { me.make_delivery_note_based_on_delivery_date(); }, __("Make"));
|
||||
this.frm.add_custom_button(__('Work Order'),
|
||||
function() { me.make_work_order() }, __("Make"));
|
||||
|
||||
if(["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1){
|
||||
this.frm.add_custom_button(__('Work Order'),
|
||||
function() { me.make_work_order() }, __("Make"));
|
||||
|
||||
}
|
||||
this.frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,17 +31,25 @@ frappe.listview_settings['Sales Order'] = {
|
||||
"per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
|
||||
}
|
||||
|
||||
} else if ((doc.order_type === "Maintenance" || flt(doc.per_delivered, 6) == 100)
|
||||
} else if ((flt(doc.per_delivered, 6) == 100)
|
||||
&& flt(doc.grand_total) !== 0 && flt(doc.per_billed, 6) < 100 && doc.status !== "Closed") {
|
||||
// to bill
|
||||
|
||||
return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
|
||||
|
||||
} else if ((doc.order_type === "Maintenance" || flt(doc.per_delivered, 6) == 100)
|
||||
} else if ((flt(doc.per_delivered, 6) === 100)
|
||||
&& (flt(doc.grand_total) === 0 || flt(doc.per_billed, 6) == 100) && doc.status !== "Closed") {
|
||||
|
||||
return [__("Completed"), "green", "per_delivered,=,100|per_billed,=,100|status,!=,Closed"];
|
||||
|
||||
}else if (doc.order_type === "Maintenance" && flt(doc.per_delivered, 6) < 100 && doc.status !== "Closed"){
|
||||
|
||||
if(flt(doc.per_billed, 6) < 100 ){
|
||||
return [__("To Deliver and Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
|
||||
}else if(flt(doc.per_billed, 6) == 100){
|
||||
return [__("To Deliver"), "orange", "per_delivered,=,100|per_billed,=,100|status,!=,Closed"];
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
onload: function(listview) {
|
||||
var method = "erpnext.selling.doctype.sales_order.sales_order.close_or_unclose_sales_orders";
|
||||
|
||||
@@ -123,6 +123,44 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||
|
||||
def test_return_against_sales_order(self):
|
||||
so = make_sales_order()
|
||||
|
||||
dn = create_dn_against_so(so.name, 6)
|
||||
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 6)
|
||||
|
||||
# Check delivered_qty after make_sales_invoice with update_stock checked
|
||||
si2 = make_sales_invoice(so.name)
|
||||
si2.set("update_stock", 1)
|
||||
si2.get("items")[0].qty = 3
|
||||
si2.insert()
|
||||
si2.submit()
|
||||
|
||||
so.load_from_db()
|
||||
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||
|
||||
# Make return deliver note, sales invoice and check quantity
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3, do_not_submit=True)
|
||||
dn1.items[0].against_sales_order = so.name
|
||||
dn1.items[0].so_detail = so.items[0].name
|
||||
dn1.submit()
|
||||
|
||||
si1 = create_sales_invoice(is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||
si1.items[0].sales_order = so.name
|
||||
si1.items[0].so_detail = so.items[0].name
|
||||
si1.submit()
|
||||
|
||||
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 5)
|
||||
|
||||
|
||||
def test_reserved_qty_for_partial_delivery(self):
|
||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||
existing_reserved_qty = get_reserved_qty()
|
||||
|
||||
@@ -11,7 +11,6 @@ def execute(filters=None):
|
||||
columns = get_columns()
|
||||
iwq_map = get_item_warehouse_quantity_map()
|
||||
item_map = get_item_details()
|
||||
|
||||
data = []
|
||||
for sbom, warehouse in iwq_map.items():
|
||||
total = 0
|
||||
@@ -20,7 +19,7 @@ def execute(filters=None):
|
||||
for wh, item_qty in warehouse.items():
|
||||
total += 1
|
||||
row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
|
||||
item_map.get(sbom).stock_uom, wh]
|
||||
item_map.get(sbom).stock_uom, wh]
|
||||
available_qty = item_qty
|
||||
total_qty += flt(available_qty)
|
||||
row += [available_qty]
|
||||
@@ -30,54 +29,38 @@ def execute(filters=None):
|
||||
if (total == len(warehouse)):
|
||||
row = ["", "", "Total", "", "", total_qty]
|
||||
data.append(row)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
|
||||
"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
|
||||
"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
|
||||
|
||||
return columns
|
||||
|
||||
def get_product_bundle_items():
|
||||
sbom_item_map = {}
|
||||
for sbom in frappe.db.sql("""select pb.new_item_code as parent, pbi.item_code, pbi.qty
|
||||
from `tabProduct Bundle Item` as pbi, `tabProduct Bundle` as pb
|
||||
where pb.docstatus < 2 and pb.name = pbi.parent""", as_dict=1):
|
||||
sbom_item_map.setdefault(sbom.parent, {}).setdefault(sbom.item_code, sbom.qty)
|
||||
|
||||
return sbom_item_map
|
||||
|
||||
def get_item_details():
|
||||
item_map = {}
|
||||
for item in frappe.db.sql("""select name, item_name, description, stock_uom
|
||||
from `tabItem`""", as_dict=1):
|
||||
item_map.setdefault(item.name, item)
|
||||
|
||||
for item in frappe.db.sql("""SELECT name, item_name, description, stock_uom
|
||||
from `tabItem`""", as_dict=1):
|
||||
item_map.setdefault(item.name, item)
|
||||
return item_map
|
||||
|
||||
def get_item_warehouse_quantity():
|
||||
iwq_map = {}
|
||||
bin = frappe.db.sql("""select item_code, warehouse, actual_qty from `tabBin`
|
||||
where actual_qty > 0""")
|
||||
for item, wh, qty in bin:
|
||||
iwq_map.setdefault(item, {}).setdefault(wh, qty)
|
||||
|
||||
return iwq_map
|
||||
|
||||
def get_item_warehouse_quantity_map():
|
||||
query = """SELECT parent, warehouse, MIN(qty) AS qty
|
||||
FROM (SELECT b.parent, bi.item_code, bi.warehouse,
|
||||
sum(bi.projected_qty) / b.qty AS qty
|
||||
FROM tabBin AS bi, (SELECT b.parent, b.item_code, b.qty, w.name
|
||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w) AS b
|
||||
FROM tabBin AS bi, (SELECT pb.new_item_code as parent, b.item_code, b.qty, w.name
|
||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w,
|
||||
`tabProduct Bundle` pb
|
||||
where b.parent = pb.name) AS b
|
||||
WHERE bi.item_code = b.item_code
|
||||
AND bi.warehouse = b.name
|
||||
GROUP BY b.parent, b.item_code, bi.warehouse
|
||||
UNION ALL
|
||||
SELECT b.parent, b.item_code, b.name, 0 AS qty
|
||||
FROM (SELECT b.parent, b.item_code, b.qty, w.name
|
||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w) AS b
|
||||
FROM (SELECT pb.new_item_code as parent, b.item_code, b.qty, w.name
|
||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w,
|
||||
`tabProduct Bundle` pb
|
||||
where b.parent = pb.name) AS b
|
||||
WHERE NOT EXISTS(SELECT *
|
||||
FROM `tabBin` AS bi
|
||||
WHERE bi.item_code = b.item_code
|
||||
@@ -92,4 +75,4 @@ def get_item_warehouse_quantity_map():
|
||||
last_sbom = line.get("parent")
|
||||
actual_dict = sbom_map.setdefault(last_sbom, {})
|
||||
actual_dict.setdefault(line.get("warehouse"), line.get("qty"))
|
||||
return sbom_map
|
||||
return sbom_map
|
||||
@@ -3127,7 +3127,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-05-12 15:26:11.503507",
|
||||
"modified": "2019-05-21 15:26:11.503507",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
||||
@@ -70,7 +70,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
||||
limit=context.page_length + 1, search=frappe.form_dict.get("search")),
|
||||
"parents": get_parent_item_groups(self.parent_item_group),
|
||||
"title": self.name,
|
||||
"products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list'))
|
||||
"products_as_list": cint(frappe.db.get_single_value('Products Settings', 'products_as_list'))
|
||||
})
|
||||
|
||||
if self.slideshow:
|
||||
@@ -114,8 +114,9 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
|
||||
data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1)
|
||||
data = adjust_qty_for_expired_items(data)
|
||||
|
||||
for item in data:
|
||||
set_product_info_for_website(item)
|
||||
if cint(frappe.db.get_single_value("Shopping Cart Settings", "enabled")):
|
||||
for item in data:
|
||||
set_product_info_for_website(item)
|
||||
|
||||
return [get_item_for_list_in_html(r) for r in data]
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ class TestBatch(unittest.TestCase):
|
||||
item = item_name,
|
||||
batch_id = batch_name
|
||||
)).insert(ignore_permissions=True)
|
||||
batch.submit()
|
||||
batch.save()
|
||||
|
||||
stock_entry = frappe.get_doc(dict(
|
||||
doctype = 'Stock Entry',
|
||||
|
||||
@@ -52,16 +52,23 @@ class DeliveryNote(SellingController):
|
||||
'percent_join_field': 'against_sales_invoice',
|
||||
'overflow_type': 'delivery',
|
||||
'no_tolerance': 1
|
||||
},
|
||||
{
|
||||
'source_dt': 'Delivery Note Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||
}]
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Delivery Note Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Sales Invoice Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'so_detail',
|
||||
'extra_cond': """ and exists (select name from `tabDelivery Note`
|
||||
where name=`tabDelivery Note Item`.parent and is_return=1)""",
|
||||
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
|
||||
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||
})
|
||||
|
||||
def before_print(self):
|
||||
def toggle_print_hide(meta, fieldname):
|
||||
|
||||
@@ -365,10 +365,18 @@ $.extend(erpnext.item, {
|
||||
show_modal_for_manufacturers: function(frm) {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
fields: [
|
||||
{fieldtype:'Link', options:'Manufacturer',
|
||||
reqd:1, label:'Manufacturer'},
|
||||
{fieldtype:'Data', label:'Manufacturer Part Number',
|
||||
fieldname: 'manufacturer_part_no'},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'manufacturer',
|
||||
options: 'Manufacturer',
|
||||
label: 'Manufacturer',
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Manufacturer Part Number',
|
||||
fieldname: 'manufacturer_part_no'
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
@@ -379,7 +387,7 @@ $.extend(erpnext.item, {
|
||||
// call the server to make the variant
|
||||
data.template = frm.doc.name;
|
||||
frappe.call({
|
||||
method:"erpnext.controllers.item_variant.get_variant",
|
||||
method: "erpnext.controllers.item_variant.get_variant",
|
||||
args: data,
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
@@ -454,7 +462,7 @@ $.extend(erpnext.item, {
|
||||
|
||||
me.multiple_variant_dialog.hide();
|
||||
frappe.call({
|
||||
method:"erpnext.controllers.item_variant.enqueue_multiple_variant_creation",
|
||||
method: "erpnext.controllers.item_variant.enqueue_multiple_variant_creation",
|
||||
args: {
|
||||
"item": frm.doc.name,
|
||||
"args": selected_attributes
|
||||
@@ -504,9 +512,9 @@ $.extend(erpnext.item, {
|
||||
let p = new Promise(resolve => {
|
||||
if(!d.numeric_values) {
|
||||
frappe.call({
|
||||
method:"frappe.client.get_list",
|
||||
args:{
|
||||
doctype:"Item Attribute Value",
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "Item Attribute Value",
|
||||
filters: [
|
||||
["parent","=", d.attribute]
|
||||
],
|
||||
@@ -524,9 +532,9 @@ $.extend(erpnext.item, {
|
||||
});
|
||||
} else {
|
||||
frappe.call({
|
||||
method:"frappe.client.get",
|
||||
args:{
|
||||
doctype:"Item Attribute",
|
||||
method: "frappe.client.get",
|
||||
args: {
|
||||
doctype: "Item Attribute",
|
||||
name: d.attribute
|
||||
}
|
||||
}).then((r) => {
|
||||
@@ -589,7 +597,7 @@ $.extend(erpnext.item, {
|
||||
var args = d.get_values();
|
||||
if(!args) return;
|
||||
frappe.call({
|
||||
method:"erpnext.controllers.item_variant.get_variant",
|
||||
method: "erpnext.controllers.item_variant.get_variant",
|
||||
args: {
|
||||
"template": frm.doc.name,
|
||||
"args": d.get_values()
|
||||
@@ -611,7 +619,7 @@ $.extend(erpnext.item, {
|
||||
} else {
|
||||
d.hide();
|
||||
frappe.call({
|
||||
method:"erpnext.controllers.item_variant.create_variant",
|
||||
method: "erpnext.controllers.item_variant.create_variant",
|
||||
args: {
|
||||
"item": frm.doc.name,
|
||||
"args": d.get_values()
|
||||
@@ -649,8 +657,8 @@ $.extend(erpnext.item, {
|
||||
.on('input', function(e) {
|
||||
var term = e.target.value;
|
||||
frappe.call({
|
||||
method:"erpnext.stock.doctype.item.item.get_item_attribute",
|
||||
args:{
|
||||
method: "erpnext.stock.doctype.item.item.get_item_attribute",
|
||||
args: {
|
||||
parent: i,
|
||||
attribute_value: term
|
||||
},
|
||||
@@ -712,7 +720,7 @@ frappe.ui.form.on("UOM Conversion Detail", {
|
||||
var row = locals[cdt][cdn];
|
||||
if (row.uom) {
|
||||
frappe.call({
|
||||
method:"erpnext.stock.doctype.item.item.get_uom_conv_factor",
|
||||
method: "erpnext.stock.doctype.item.item.get_uom_conv_factor",
|
||||
args: {
|
||||
"uom": row.uom,
|
||||
"stock_uom": frm.doc.stock_uom
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -691,7 +691,18 @@ class Item(WebsiteGenerator):
|
||||
'income_account': item.income_account
|
||||
})
|
||||
else:
|
||||
self.append("item_defaults", {"company": frappe.defaults.get_defaults().company})
|
||||
warehouse = ''
|
||||
defaults = frappe.defaults.get_defaults() or {}
|
||||
|
||||
# To check default warehouse is belong to the default company
|
||||
if defaults.get("default_warehouse") and frappe.db.exists("Warehouse",
|
||||
{'name': defaults.default_warehouse, 'company': defaults.company}):
|
||||
warehouse = defaults.default_warehouse
|
||||
|
||||
self.append("item_defaults", {
|
||||
"company": defaults.get("company"),
|
||||
"default_warehouse": warehouse
|
||||
})
|
||||
|
||||
def update_variants(self):
|
||||
if self.flags.dont_update_variants or \
|
||||
|
||||
@@ -38,6 +38,10 @@ frappe.ui.form.on('Material Request', {
|
||||
};
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.events.make_custom_buttons(frm);
|
||||
},
|
||||
|
||||
@@ -231,6 +231,8 @@ def update_completed_and_requested_qty(stock_entry, method):
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
def set_missing_values(source, target_doc):
|
||||
if target_doc.doctype == "Purchase Order" and getdate(target_doc.schedule_date) < getdate(nowdate()):
|
||||
target_doc.schedule_date = None
|
||||
target_doc.run_method("set_missing_values")
|
||||
target_doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
@@ -238,6 +240,8 @@ def update_item(obj, target, source_parent):
|
||||
target.conversion_factor = obj.conversion_factor
|
||||
target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor
|
||||
target.stock_qty = (target.qty * target.conversion_factor)
|
||||
if getdate(target.schedule_date) < getdate(nowdate()):
|
||||
target.schedule_date = None
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_status(name, status):
|
||||
@@ -322,7 +326,8 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None):
|
||||
|
||||
def postprocess(source, target_doc):
|
||||
target_doc.supplier = source_name
|
||||
target_doc.schedule_date = add_days(nowdate(), 1)
|
||||
if getdate(target_doc.schedule_date) < getdate(nowdate()):
|
||||
target_doc.schedule_date = None
|
||||
target_doc.set("items", [d for d in target_doc.get("items")
|
||||
if d.get("item_code") in supplier_items and d.get("qty") > 0])
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
});
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.company) {
|
||||
frm.trigger("toggle_display_account_head");
|
||||
|
||||
@@ -24,29 +24,37 @@ class PurchaseReceipt(BuyingController):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'purchase_order_item',
|
||||
'target_field': 'received_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
'target_parent_field': 'per_received',
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'received_qty',
|
||||
'percent_join_field': 'purchase_order',
|
||||
'overflow_type': 'receipt'
|
||||
},
|
||||
{
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'purchase_order_item',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
# 'target_parent_field': 'per_received',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
||||
'source_field': 'received_qty',
|
||||
'second_source_dt': 'Purchase Invoice Item',
|
||||
'second_source_field': 'received_qty',
|
||||
'second_join_field': 'po_detail',
|
||||
'percent_join_field': 'purchase_order',
|
||||
'overflow_type': 'receipt',
|
||||
'second_source_extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||
}]
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'purchase_order_item',
|
||||
'target_field': 'returned_qty',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Purchase Invoice Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'po_detail',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt`
|
||||
where name=`tabPurchase Receipt Item`.parent and is_return=1)""",
|
||||
'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||
})
|
||||
|
||||
def validate(self):
|
||||
self.validate_posting_time()
|
||||
|
||||
@@ -579,9 +579,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
});
|
||||
|
||||
// if(!this.item_selector && false) {
|
||||
// this.item_selector = new erpnext.ItemSelector({frm: this.frm});
|
||||
// }
|
||||
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, add_additional_cost
|
||||
from erpnext.stock.utils import get_bin
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
|
||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
|
||||
|
||||
import json
|
||||
|
||||
@@ -59,6 +60,7 @@ class StockEntry(StockController):
|
||||
self.validate_batch()
|
||||
self.validate_inspection()
|
||||
self.validate_fg_completed_qty()
|
||||
self.validate_difference_account()
|
||||
self.set_job_card_data()
|
||||
|
||||
if not self.from_bom:
|
||||
@@ -215,7 +217,18 @@ class StockEntry(StockController):
|
||||
production_item = frappe.get_value('Work Order', self.work_order, 'production_item')
|
||||
for item in self.items:
|
||||
if item.item_code == production_item and item.qty != self.fg_completed_qty:
|
||||
frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different").format(item.qty, self.fg_completed_qty))
|
||||
frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different")
|
||||
.format(item.qty, self.fg_completed_qty))
|
||||
|
||||
def validate_difference_account(self):
|
||||
if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||
return
|
||||
|
||||
for d in self.get("items"):
|
||||
if not d.expense_account:
|
||||
frappe.throw(_("Please enter Difference Account"))
|
||||
elif self.is_opening == "Yes" and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss":
|
||||
frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"), OpeningEntryAccountError)
|
||||
|
||||
def validate_warehouse(self):
|
||||
"""perform various (sometimes conditional) validations on warehouse"""
|
||||
|
||||
@@ -89,10 +89,11 @@ def make_stock_entry(**args):
|
||||
s.purchase_receipt_no = args.purchase_receipt_no
|
||||
s.delivery_note_no = args.delivery_note_no
|
||||
s.sales_invoice_no = args.sales_invoice_no
|
||||
s.is_opening = args.is_opening or "No"
|
||||
if not args.cost_center:
|
||||
args.cost_center = frappe.get_value('Company', s.company, 'cost_center')
|
||||
|
||||
if not args.expense_account:
|
||||
if not args.expense_account and s.is_opening == "No":
|
||||
args.expense_account = frappe.get_value('Company', s.company, 'stock_adjustment_account')
|
||||
|
||||
# We can find out the serial number using the batch source document
|
||||
|
||||
@@ -6,8 +6,7 @@ import frappe, unittest
|
||||
import frappe.defaults
|
||||
from frappe.utils import flt, nowdate, nowtime
|
||||
from erpnext.stock.doctype.serial_no.serial_no import *
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import set_perpetual_inventory
|
||||
from erpnext import set_perpetual_inventory
|
||||
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
from frappe.permissions import add_user_permission, remove_user_permission
|
||||
@@ -16,6 +15,7 @@ from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse
|
||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
|
||||
|
||||
from six import iteritems
|
||||
|
||||
@@ -720,6 +720,22 @@ class TestStockEntry(unittest.TestCase):
|
||||
for d in stock_entry.get('items'):
|
||||
self.assertEqual(item_quantity.get(d.item_code), d.qty)
|
||||
|
||||
def test_gle_for_opening_stock_entry(self):
|
||||
set_perpetual_inventory(1)
|
||||
|
||||
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
|
||||
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC", is_opening="Yes", do_not_save=True)
|
||||
|
||||
self.assertRaises(OpeningEntryAccountError, mr.save)
|
||||
|
||||
mr.items[0].expense_account = "Temporary Opening - _TC"
|
||||
mr.save()
|
||||
mr.submit()
|
||||
|
||||
is_opening = frappe.db.get_value("GL Entry",
|
||||
filters={"voucher_type": "Stock Entry", "voucher_no": mr.name}, fieldname="is_opening")
|
||||
self.assertEqual(is_opening, "Yes")
|
||||
|
||||
def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
|
||||
se = frappe.copy_doc(test_records[0])
|
||||
se.get("items")[0].item_code = item_code or "_Test Serialized Item With Series"
|
||||
|
||||
@@ -238,8 +238,8 @@ class StockReconciliation(StockController):
|
||||
return
|
||||
|
||||
if not self.expense_account:
|
||||
msgprint(_("Please enter Expense Account"), raise_exception=1)
|
||||
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
|
||||
frappe.throw(_("Please enter Expense Account"))
|
||||
elif self.purpose == "Opening Stock" or not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
|
||||
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
|
||||
frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
|
||||
|
||||
@@ -276,7 +276,8 @@ def get_items(warehouse, posting_date, posting_time, company):
|
||||
items = frappe.db.sql("""
|
||||
select i.name, i.item_name, bin.warehouse
|
||||
from tabBin bin, tabItem i
|
||||
where i.name=bin.item_code and i.disabled=0
|
||||
where i.name=bin.item_code and i.disabled=0 and i.is_stock_item = 1
|
||||
and i.has_variants = 0 and i.has_serial_no = 0 and i.has_batch_no = 0
|
||||
and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse)
|
||||
""", (lft, rgt))
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ class Warehouse(NestedSet):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_children(doctype, parent=None, company=None, is_root=False):
|
||||
from erpnext.stock.utils import get_stock_value_on
|
||||
from erpnext.stock.utils import get_stock_value_from_bin
|
||||
|
||||
if is_root:
|
||||
parent = ""
|
||||
@@ -156,7 +156,7 @@ def get_children(doctype, parent=None, company=None, is_root=False):
|
||||
|
||||
# return warehouses
|
||||
for wh in warehouses:
|
||||
wh["balance"] = get_stock_value_on(warehouse=wh.value, posting_date=nowdate())
|
||||
wh["balance"] = get_stock_value_from_bin(warehouse=wh.value)
|
||||
if company:
|
||||
wh["company_currency"] = frappe.db.get_value('Company', company, 'default_currency')
|
||||
return warehouses
|
||||
|
||||
@@ -306,8 +306,21 @@ def get_basic_details(args, item):
|
||||
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
|
||||
out[fieldname] = item.get(fieldname)
|
||||
|
||||
child_doctype = args.doctype + ' Item'
|
||||
meta = frappe.get_meta(child_doctype)
|
||||
if meta.get_field("barcode"):
|
||||
update_barcode_value(out)
|
||||
|
||||
return out
|
||||
|
||||
def update_barcode_value(out):
|
||||
from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data
|
||||
barcode_data = get_barcode_data([out])
|
||||
|
||||
# If item has one barcode then update the value of the barcode field
|
||||
if barcode_data and len(barcode_data.get(out.item_code)) == 1:
|
||||
out['barcode'] = barcode_data.get(out.item_code)[0]
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_service_end_date(args, item=None):
|
||||
args = process_args(args)
|
||||
@@ -377,7 +390,7 @@ def get_price_list_rate(args, item_doc, out):
|
||||
pl_details = get_price_list_currency_and_exchange_rate(args)
|
||||
args.update(pl_details)
|
||||
validate_price_list(args)
|
||||
if meta.get_field("currency") and args.price_list:
|
||||
if meta.get_field("currency"):
|
||||
validate_conversion_rate(args, meta)
|
||||
|
||||
price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
|
||||
@@ -554,27 +567,28 @@ def validate_conversion_rate(args, meta):
|
||||
get_field_precision(meta.get_field("conversion_rate"),
|
||||
frappe._dict({"fields": args})))
|
||||
|
||||
if (not args.plc_conversion_rate
|
||||
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
||||
args.plc_conversion_rate = 1.0
|
||||
if args.price_list:
|
||||
if (not args.plc_conversion_rate
|
||||
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
||||
args.plc_conversion_rate = 1.0
|
||||
|
||||
# validate price list currency conversion rate
|
||||
if not args.get("price_list_currency"):
|
||||
throw(_("Price List Currency not selected"))
|
||||
else:
|
||||
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
||||
meta.get_label("plc_conversion_rate"), args.company)
|
||||
# validate price list currency conversion rate
|
||||
if not args.get("price_list_currency"):
|
||||
throw(_("Price List Currency not selected"))
|
||||
else:
|
||||
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
||||
meta.get_label("plc_conversion_rate"), args.company)
|
||||
|
||||
if meta.get_field("plc_conversion_rate"):
|
||||
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
||||
get_field_precision(meta.get_field("plc_conversion_rate"),
|
||||
frappe._dict({"fields": args})))
|
||||
if meta.get_field("plc_conversion_rate"):
|
||||
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
||||
get_field_precision(meta.get_field("plc_conversion_rate"),
|
||||
frappe._dict({"fields": args})))
|
||||
|
||||
def get_party_item_code(args, item_doc, out):
|
||||
if args.transaction_type=="selling" and args.customer:
|
||||
out.customer_item_code = None
|
||||
|
||||
if args.quotation_to != 'Customer':
|
||||
if args.quotation_to and args.quotation_to != 'Customer':
|
||||
return
|
||||
|
||||
customer_item_code = item_doc.get("customer_items", {"customer_name": args.customer})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Order Summary"] = {
|
||||
frappe.query_reports["Delayed Item Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "company",
|
||||
@@ -55,7 +55,7 @@ frappe.query_reports["Delayed Order Summary"] = {
|
||||
label: __("Based On"),
|
||||
fieldtype: "Select",
|
||||
options: ["Delivery Note", "Sales Invoice"],
|
||||
default: "Sales Invoice",
|
||||
default: "Delivery Note",
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-05-12 12:38:44.907187",
|
||||
"creation": "2019-05-27 19:11:50.605376",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
@@ -8,14 +8,14 @@
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2019-05-12 12:39:16.378341",
|
||||
"modified": "2019-05-27 19:11:50.605376",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delayed Order Summary",
|
||||
"name": "Delayed Item Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Delivery Note",
|
||||
"report_name": "Delayed Order Summary",
|
||||
"report_name": "Delayed Item Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
@@ -7,11 +7,11 @@ from frappe import _
|
||||
from frappe.utils import date_diff
|
||||
|
||||
def execute(filters=None, consolidated = False):
|
||||
data, columns = DelayedOrderItemSummary(filters).run()
|
||||
data, columns = DelayedItemReport(filters).run()
|
||||
|
||||
return data, columns
|
||||
|
||||
class DelayedOrderItemSummary(object):
|
||||
class DelayedItemReport(object):
|
||||
def __init__(self, filters=None):
|
||||
self.filters = frappe._dict(filters or {})
|
||||
|
||||
@@ -83,7 +83,7 @@ class DelayedOrderItemSummary(object):
|
||||
key = row.sales_order if consolidated else (row.sales_order, row.so_detail)
|
||||
row.update({
|
||||
'delivery_date': so_data.get(key),
|
||||
'delay_days': date_diff(row.posting_date, so_data.get(key))
|
||||
'delayed_days': date_diff(row.posting_date, so_data.get(key))
|
||||
})
|
||||
|
||||
return self.transactions
|
||||
@@ -148,8 +148,8 @@ class DelayedOrderItemSummary(object):
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Delay Days"),
|
||||
"fieldname": "delay_days",
|
||||
"label": _("Delayed Days"),
|
||||
"fieldname": "delayed_days",
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
},
|
||||
@@ -2,7 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Order Item Summary"] = {
|
||||
frappe.query_reports["Delayed Order Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "company",
|
||||
@@ -55,7 +55,7 @@ frappe.query_reports["Delayed Order Item Summary"] = {
|
||||
label: __("Based On"),
|
||||
fieldtype: "Select",
|
||||
options: ["Delivery Note", "Sales Invoice"],
|
||||
default: "Sales Invoice",
|
||||
default: "Delivery Note",
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-05-12 13:05:39.162734",
|
||||
"creation": "2019-05-27 19:12:24.719610",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
@@ -8,14 +8,14 @@
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2019-05-12 13:05:39.162734",
|
||||
"modified": "2019-05-27 19:12:24.719610",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delayed Order Item Summary",
|
||||
"name": "Delayed Order Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Delivery Note",
|
||||
"report_name": "Delayed Order Item Summary",
|
||||
"report_name": "Delayed Order Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
@@ -3,21 +3,21 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
from erpnext.stock.report.delayed_order_item_summary.delayed_order_item_summary import DelayedOrderItemSummary
|
||||
from erpnext.stock.report.delayed_item_report.delayed_item_report import DelayedItemReport
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
|
||||
columns, data = DelayedOrderSummary(filters).run()
|
||||
columns, data = DelayedOrderReport(filters).run()
|
||||
|
||||
return columns, data
|
||||
|
||||
class DelayedOrderSummary(DelayedOrderItemSummary):
|
||||
class DelayedOrderReport(DelayedItemReport):
|
||||
def run(self):
|
||||
return self.get_columns(), self.get_data(consolidated=True) or []
|
||||
|
||||
def get_data(self, consolidated=False):
|
||||
data = super(DelayedOrderSummary, self).get_data(consolidated) or []
|
||||
data = super(DelayedOrderReport, self).get_data(consolidated) or []
|
||||
|
||||
so_list = []
|
||||
result = []
|
||||
@@ -63,8 +63,8 @@ class DelayedOrderSummary(DelayedOrderItemSummary):
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Delay Days"),
|
||||
"fieldname": "delay_days",
|
||||
"label": _("Delayed Days"),
|
||||
"fieldname": "delayed_days",
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
},
|
||||
Reference in New Issue
Block a user