diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index e11c0c39701..5d73e79035c 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -209,7 +209,7 @@ class TestSubscription(unittest.TestCase):
subscription = frappe.new_doc('Subscription')
subscription.customer = '_Test Customer'
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
- subscription.start = '2018-01-01'
+ subscription.start = add_days(nowdate(), -1000)
subscription.days_until_due = 1
subscription.insert()
subscription.process() # generate first invoice
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 12e7b8b8c37..4e7922c6fbe 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -60,7 +60,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
billing_address=party_address, shipping_address=shipping_address)
if fetch_payment_terms_template:
- party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
+ party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
if not party_details.get("currency"):
party_details["currency"] = currency
@@ -318,7 +318,7 @@ def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
due_date = None
if (bill_date or posting_date) and party:
due_date = bill_date or posting_date
- template_name = get_pyt_term_template(party, party_type, company)
+ template_name = get_payment_terms_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
@@ -425,7 +425,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
@frappe.whitelist()
-def get_pyt_term_template(party_name, party_type, company=None):
+def get_payment_terms_template(party_name, party_type, company=None):
if party_type not in ("Customer", "Supplier"):
return
template = None
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index f6632fa2632..08695e7f0d5 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -160,6 +160,8 @@ class ReceivablePayableReport(object):
else:
# advance / unlinked payment or other adjustment
row.paid -= gle_balance
+ if gle.cost_center:
+ row.cost_center = gle.cost_center
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
@@ -210,7 +212,6 @@ class ReceivablePayableReport(object):
for key, row in self.voucher_balance.items():
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
row.invoice_grand_total = row.invoiced
-
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
# non-zero oustanding, we must consider this row
@@ -577,7 +578,7 @@ class ReceivablePayableReport(object):
self.gl_entries = frappe.db.sql("""
select
- name, posting_date, account, party_type, party, voucher_type, voucher_no,
+ name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
against_voucher_type, against_voucher, account_currency, remarks, {0}
from
`tabGL Entry`
@@ -741,6 +742,7 @@ class ReceivablePayableReport(object):
self.add_column(_("Customer Contact"), fieldname='customer_primary_contact',
fieldtype='Link', options='Contact')
+ self.add_column(label=_('Cost Center'), fieldname='cost_center', fieldtype='Data')
self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data')
self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link',
options='voucher_type', width=180)
diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
index 3ffb3ac1df4..2aea3f64239 100644
--- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
+++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py
@@ -14,11 +14,19 @@ def execute(filters=None):
def get_column():
return [
- _("Delivery Note") + ":Link/Delivery Note:120", _("Status") + "::120", _("Date") + ":Date:100",
- _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
- _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
- _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
- _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
+ _("Delivery Note") + ":Link/Delivery Note:160",
+ _("Date") + ":Date:100",
+ _("Customer") + ":Link/Customer:120",
+ _("Customer Name") + "::120",
+ _("Item Code") + ":Link/Item:120",
+ _("Amount") + ":Currency:100",
+ _("Billed Amount") + ":Currency:100",
+ _("Returned Amount") + ":Currency:120",
+ _("Pending Amount") + ":Currency:100",
+ _("Item Name") + "::120",
+ _("Description") + "::120",
+ _("Project") + ":Link/Project:120",
+ _("Company") + ":Link/Company:120",
]
def get_args():
diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py
index a9e25bc25bf..2e18ce11ddc 100644
--- a/erpnext/accounts/report/non_billed_report.py
+++ b/erpnext/accounts/report/non_billed_report.py
@@ -17,18 +17,26 @@ def get_ordered_to_be_billed_data(args):
return frappe.db.sql("""
Select
- `{parent_tab}`.name, `{parent_tab}`.status, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
- {project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount,
+ `{parent_tab}`.name, `{parent_tab}`.{date_field},
+ `{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
+ `{child_tab}`.item_code,
+ `{child_tab}`.base_amount,
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
- (`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))),
- `{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company
+ (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)),
+ (`{child_tab}`.base_amount -
+ (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) -
+ (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))),
+ `{child_tab}`.item_name, `{child_tab}`.description,
+ {project_field}, `{parent_tab}`.company
from
`{parent_tab}`, `{child_tab}`
where
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
and `{parent_tab}`.status not in ('Closed', 'Completed')
- and `{child_tab}`.amount > 0 and round(`{child_tab}`.billed_amt *
- ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) < `{child_tab}`.base_amount
+ and `{child_tab}`.amount > 0
+ and (`{child_tab}`.base_amount -
+ round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) -
+ (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
order by
`{parent_tab}`.{order} {order_by}
""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
index 5e8d7730b76..c7d4384a734 100644
--- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
+++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py
@@ -14,11 +14,19 @@ def execute(filters=None):
def get_column():
return [
- _("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Status") + "::120", _("Date") + ":Date:100",
- _("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120",
- _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
- _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
- _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
+ _("Purchase Receipt") + ":Link/Purchase Receipt:160",
+ _("Date") + ":Date:100",
+ _("Supplier") + ":Link/Supplier:120",
+ _("Supplier Name") + "::120",
+ _("Item Code") + ":Link/Item:120",
+ _("Amount") + ":Currency:100",
+ _("Billed Amount") + ":Currency:100",
+ _("Returned Amount") + ":Currency:120",
+ _("Pending Amount") + ":Currency:120",
+ _("Item Name") + "::120",
+ _("Description") + "::120",
+ _("Project") + ":Link/Project:120",
+ _("Company") + ":Link/Company:120",
]
def get_args():
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index 8a954b94d1e..557246e7efc 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -108,7 +108,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_team_members(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") })
+ return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }, "team_member")
@frappe.whitelist()
def get_maintenance_log(asset_name):
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index cb745d6dba8..be5763b1d9c 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -855,7 +855,7 @@ class TestPurchaseOrder(unittest.TestCase):
},
{
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
- "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name
+ "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
},
]
@@ -864,6 +864,10 @@ class TestPurchaseOrder(unittest.TestCase):
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
se.submit()
+ # Test po_detail field has value or not
+ for item_row in se.items:
+ self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
+
po_doc = frappe.get_doc("Purchase Order", po.name)
for row in po_doc.supplied_items:
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 6e05a312352..68fc331e218 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -292,7 +292,7 @@ class BuyingController(StockController):
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
for raw_material in transferred_raw_materials + non_stock_items:
- rm_item_key = (raw_material.rm_item_code, item.purchase_order)
+ rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
consumed_qty = raw_material_data.get('qty', 0)
@@ -881,7 +881,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
for data in purchase_receipt_supplied_items:
- pr_key = (data.rm_item_code, args[0])
+ pr_key = (data.rm_item_code, data.main_item_code, args[0])
if pr_key not in backflushed_raw_materials_map:
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
"qty": 0.0,
@@ -907,7 +907,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
def get_supplied_items(item_code, purchase_receipt, references):
return frappe.get_all("Purchase Receipt Item Supplied",
- fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"],
+ fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"],
filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
def get_asset_item_details(asset_items):
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 7b274119244..4262fd3a10f 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", {
make_dashboard: function(frm) {
var leave_details;
+ let lwps;
if (frm.doc.employee) {
frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
@@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", {
if (!r.exc && r.message['leave_approver']) {
frm.set_value('leave_approver', r.message['leave_approver']);
}
+ lwps = r.message["lwps"];
}
});
$("div").remove(".form-dashboard-section");
@@ -70,6 +72,18 @@ frappe.ui.form.on("Leave Application", {
})
);
frm.dashboard.show();
+ let allowed_leave_types = Object.keys(leave_details);
+
+ // lwps should be allowed, lwps don't have any allocation
+ allowed_leave_types = allowed_leave_types.concat(lwps);
+
+ frm.set_query('leave_type', function(){
+ return {
+ filters : [
+ ['leave_type_name', 'in', allowed_leave_types]
+ ]
+ };
+ });
}
},
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index cac4f33a237..aa7d7316b48 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
-
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@@ -451,9 +450,14 @@ def get_leave_details(employee, date):
"pending_leaves": leaves_pending,
"remaining_leaves": remaining_leaves}
+ #is used in set query
+ lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
+ lwps = [lwp.name for lwp in lwps]
+
ret = {
'leave_allocation': leave_allocation,
- 'leave_approver': get_leave_approver(employee)
+ 'leave_approver': get_leave_approver(employee),
+ 'lwps': lwps
}
return ret
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
index 295f3b43419..d30e3b9f9c6 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
@@ -1,5 +1,5 @@
-{% if data %}
+{% if not jQuery.isEmptyObject(data) %}
{{ __("Allocated Leaves") }}
@@ -11,7 +11,6 @@
| {{ __("Pending Leaves") }} |
{{ __("Available Leaves") }} |
-
{% for(const [key, value] of Object.entries(data)) { %}
@@ -26,6 +25,6 @@
{% } %}
-{% } else { %}
+{% else %}
No Leaves have been allocated.
-{% } %}
\ No newline at end of file
+{% endif %}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
index cf07698ad6a..f4877fdca0b 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
@@ -8,7 +8,17 @@ frappe.views.calendar["Job Card"] = {
"allDay": "allDay",
"progress": "progress"
},
- gantt: true,
+ gantt: {
+ field_map: {
+ "start": "started_time",
+ "end": "started_time",
+ "id": "name",
+ "title": "subject",
+ "color": "color",
+ "allDay": "allDay",
+ "progress": "progress"
+ }
+ },
filters: [
{
"fieldtype": "Link",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index aa80dcfed24..5a193d7e4bf 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -709,8 +709,12 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
mr_items.append(items)
if not mr_items:
- frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, there is no need to create material request.
- Still if you want to make material request, kindly enable Ignore Existing Projected Quantity checkbox"""))
+ to_enable = frappe.bold(_("Ignore Existing Projected Quantity"))
+ warehouse = frappe.bold(doc.get('for_warehouse'))
+ message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "
"
+ message += _(" If you still want to proceed, please enable {0}.").format(to_enable)
+
+ frappe.msgprint(message, title=_("Note"))
return mr_items
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index d82a4dd9fe8..0263102bac0 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -193,6 +193,42 @@ class TestWorkOrder(unittest.TestCase):
self.assertEqual(cint(bin1_on_end_production.projected_qty),
cint(bin1_on_end_production.projected_qty))
+ def test_backflush_qty_for_overpduction_manufacture(self):
+ cancel_stock_entry = []
+ allow_overproduction("overproduction_percentage_for_work_order", 30)
+ wo_order = make_wo_order_test_record(planned_start_date=now(), qty=100)
+ ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
+ target="_Test Warehouse - _TC", qty=120, basic_rate=5000.0)
+ ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
+ target="_Test Warehouse - _TC", qty=240, basic_rate=1000.0)
+
+ cancel_stock_entry.extend([ste1.name, ste2.name])
+
+ s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 60))
+ s.submit()
+ cancel_stock_entry.append(s.name)
+
+ s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 60))
+ s.submit()
+ cancel_stock_entry.append(s.name)
+
+ s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 60))
+ s.submit()
+ cancel_stock_entry.append(s.name)
+
+ s1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 50))
+ s1.submit()
+ cancel_stock_entry.append(s1.name)
+
+ self.assertEqual(s1.items[0].qty, 50)
+ self.assertEqual(s1.items[1].qty, 100)
+ cancel_stock_entry.reverse()
+ for ste in cancel_stock_entry:
+ doc = frappe.get_doc("Stock Entry", ste)
+ doc.cancel()
+
+ allow_overproduction("overproduction_percentage_for_work_order", 0)
+
def test_reserved_qty_for_stopped_production(self):
test_stock_entry.make_stock_entry(item_code="_Test Item",
target= self.warehouse, qty=100, basic_rate=100)
@@ -371,6 +407,11 @@ class TestWorkOrder(unittest.TestCase):
ste1 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1))
self.assertEqual(len(ste1.items), 3)
+ def test_cost_center_for_manufacture(self):
+ wo_order = make_wo_order_test_record()
+ ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty)
+ self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC")
+
def test_operation_time_with_batch_size(self):
fg_item = "Test Batch Size Item For BOM"
rm1 = "Test Batch Size Item RM 1 For BOM"
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index afb6db35f27..b5f31bafa7e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -635,7 +635,7 @@ execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
erpnext.patches.v12_0.add_default_dashboards
erpnext.patches.v12_0.remove_bank_remittance_custom_fields
-erpnext.patches.v12_0.generate_leave_ledger_entries
+erpnext.patches.v12_0.generate_leave_ledger_entries #04-11-2020
erpnext.patches.v12_0.move_credit_limit_to_customer_credit_limit
erpnext.patches.v12_0.add_variant_of_in_item_attribute_table
erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index c5bec19fed4..7afde373c30 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -11,8 +11,6 @@ def execute():
frappe.reload_doc("HR", "doctype", "Leave Ledger Entry")
frappe.reload_doc("HR", "doctype", "Leave Encashment")
frappe.reload_doc("HR", "doctype", "Leave Type")
- if frappe.db.a_row_exists("Leave Ledger Entry"):
- return
if not frappe.get_meta("Leave Allocation").has_field("unused_leaves"):
frappe.reload_doc("HR", "doctype", "Leave Allocation")
@@ -36,8 +34,7 @@ def generate_allocation_ledger_entries():
for allocation in allocation_list:
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}):
- allocation.update(dict(doctype="Leave Allocation"))
- allocation_obj = frappe.get_doc(allocation)
+ allocation_obj = frappe.get_doc("Leave Allocation", allocation)
allocation_obj.create_leave_ledger_entry()
def generate_application_leave_ledger_entries():
@@ -46,8 +43,7 @@ def generate_application_leave_ledger_entries():
for application in leave_applications:
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}):
- application.update(dict(doctype="Leave Application"))
- frappe.get_doc(application).create_leave_ledger_entry()
+ frappe.get_doc("Leave Application", application.name).create_leave_ledger_entry()
def generate_encashment_leave_ledger_entries():
''' fix ledger entries for missing leave encashment transaction '''
@@ -55,8 +51,7 @@ def generate_encashment_leave_ledger_entries():
for encashment in leave_encashments:
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
- encashment.update(dict(doctype="Leave Encashment"))
- frappe.get_doc(encashment).create_leave_ledger_entry()
+ frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry()
def generate_expiry_allocation_ledger_entries():
''' fix ledger entries for missing leave allocation transaction '''
@@ -65,24 +60,16 @@ def generate_expiry_allocation_ledger_entries():
for allocation in allocation_list:
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name, 'is_expired': 1}):
- allocation.update(dict(doctype="Leave Allocation"))
- allocation_obj = frappe.get_doc(allocation)
+ allocation_obj = frappe.get_doc("Leave Allocation", allocation)
if allocation_obj.to_date <= getdate(today()):
expire_allocation(allocation_obj)
def get_allocation_records():
- return frappe.get_all("Leave Allocation", filters={
- "docstatus": 1
- }, fields=['name', 'employee', 'leave_type', 'new_leaves_allocated',
- 'unused_leaves', 'from_date', 'to_date', 'carry_forward'
- ], order_by='to_date ASC')
+ return frappe.get_all("Leave Allocation", filters={"docstatus": 1},
+ fields=['name'], order_by='to_date ASC')
def get_leaves_application_records():
- return frappe.get_all("Leave Application", filters={
- "docstatus": 1
- }, fields=['name', 'employee', 'leave_type', 'total_leave_days', 'from_date', 'to_date'])
+ return frappe.get_all("Leave Application", filters={"docstatus": 1}, fields=['name'])
def get_leave_encashment_records():
- return frappe.get_all("Leave Encashment", filters={
- "docstatus": 1
- }, fields=['name', 'employee', 'leave_type', 'encashable_days', 'encashment_date'])
+ return frappe.get_all("Leave Encashment", filters={"docstatus": 1}, fields=['name'])
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 88637bb4ec5..ea6a2cd2227 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -139,7 +139,7 @@ def get_place_of_supply(party_details, doctype):
if not frappe.get_meta('Address').has_field('gst_state'): return
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
- address_name = party_details.shipping_address_name or party_details.customer_address
+ address_name = party_details.customer_address or party_details.shipping_address_name
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
address_name = party_details.shipping_address or party_details.supplier_address
@@ -218,10 +218,9 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code):
for tax_category in tax_categories:
if tax_category.gst_state == number_state_mapping[state_code] or \
- (not default_tax and not tax_category.gst_state):
+ (not default_tax and not tax_category.gst_state):
default_tax = frappe.db.get_value(master_doctype,
- {'disabled': 0, 'tax_category': tax_category.name}, 'name')
-
+ {'company': company, 'disabled': 0, 'tax_category': tax_category.name}, 'name')
return default_tax
def get_tax_template_for_sez(party_details, master_doctype, company, party_type):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index a83741d3eaf..a995bfba524 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -57,7 +57,7 @@ class TestDeliveryNote(unittest.TestCase):
sle = frappe.get_doc("Stock Ledger Entry", {"voucher_type": "Delivery Note", "voucher_no": dn.name})
- self.assertEqual(sle.stock_value_difference, -1*stock_queue[0][1])
+ self.assertEqual(sle.stock_value_difference, flt(-1*stock_queue[0][1]))
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index e6634d29fe1..fe47e669261 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -983,9 +983,7 @@ class Item(WebsiteGenerator):
if self.stock_ledger_created():
return True
- elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
- frappe.db.get_value("Production Order",
- filters={"production_item": self.name, "docstatus": 1}):
+ elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}):
return True
def validate_auto_reorder_enabled_in_stock_settings(self):
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 8538b00fd6e..730a1d0c076 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -500,6 +500,8 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True):
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
+ from erpnext.accounts.party import get_payment_terms_template
+
doc = frappe.get_doc('Purchase Receipt', source_name)
returned_qty_map = get_returned_qty_map(source_name)
invoiced_qty_map = get_invoiced_qty_map(source_name)
@@ -510,6 +512,7 @@ def make_purchase_invoice(source_name, target_doc=None):
doc = frappe.get_doc(target)
doc.ignore_pricing_rule = 1
+ doc.payment_terms_template = get_payment_terms_template(source.supplier, "Supplier", source.company)
doc.run_method("onload")
doc.run_method("set_missing_values")
doc.run_method("calculate_taxes_and_totals")
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d0208d01eda..26bcd457449 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -20,6 +20,30 @@ class TestPurchaseReceipt(unittest.TestCase):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
def test_make_purchase_invoice(self):
+ if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'):
+ frappe.get_doc({
+ 'doctype': 'Payment Terms Template',
+ 'template_name': '_Test Payment Terms Template For Purchase Invoice',
+ 'allocate_payment_based_on_payment_terms': 1,
+ 'terms': [
+ {
+ 'doctype': 'Payment Terms Template Detail',
+ 'invoice_portion': 50.00,
+ 'credit_days_based_on': 'Day(s) after invoice date',
+ 'credit_days': 00
+ },
+ {
+ 'doctype': 'Payment Terms Template Detail',
+ 'invoice_portion': 50.00,
+ 'credit_days_based_on': 'Day(s) after invoice date',
+ 'credit_days': 30
+ }]
+ }).insert()
+
+ template = frappe.db.get_value('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice')
+ old_template_in_supplier = frappe.db.get_value("Supplier", "_Test Supplier", "payment_terms")
+ frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", template)
+
pr = make_purchase_receipt(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, pr.name)
pr.submit()
@@ -29,10 +53,23 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertEqual(pi.doctype, "Purchase Invoice")
self.assertEqual(len(pi.get("items")), len(pr.get("items")))
- # modify rate
+ # test maintaining same rate throughout purchade cycle
pi.get("items")[0].rate = 200
self.assertRaises(frappe.ValidationError, frappe.get_doc(pi).submit)
+ # test if payment terms are fetched and set in PI
+ self.assertEqual(pi.payment_terms_template, template)
+ self.assertEqual(pi.payment_schedule[0].payment_amount, flt(pi.grand_total)/2)
+ self.assertEqual(pi.payment_schedule[0].invoice_portion, 50)
+ self.assertEqual(pi.payment_schedule[1].payment_amount, flt(pi.grand_total)/2)
+ self.assertEqual(pi.payment_schedule[1].invoice_portion, 50)
+
+ # teardown
+ pi.delete() # draft PI
+ pr.cancel()
+ frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", old_template_in_supplier)
+ frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
+
def test_purchase_receipt_no_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 8afb3fbe8e5..1dd022fce00 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -594,6 +594,15 @@ class StockEntry(StockController):
if not row.subcontracted_item:
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
.format(row.idx, frappe.bold(row.item_code)))
+ elif not row.po_detail:
+ filters = {
+ "parent": self.purchase_order, "docstatus": 1,
+ "rm_item_code": row.item_code, "main_item_code": row.subcontracted_item
+ }
+
+ po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
+ if po_detail:
+ row.db_set("po_detail", po_detail)
def validate_bom(self):
for d in self.get('items'):
@@ -1104,7 +1113,10 @@ class StockEntry(StockController):
for d in backflushed_materials.get(item.item_code):
if d.get(item.warehouse):
if (qty > req_qty):
- qty-= d.get(item.warehouse)
+ qty = (qty/trans_qty) * flt(self.fg_completed_qty)
+
+ if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')):
+ qty = frappe.utils.ceil(qty)
if qty > 0:
self.add_to_stock_entry_detail({
@@ -1185,8 +1197,6 @@ class StockEntry(StockController):
return item_dict
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
- cost_center = frappe.db.get_value("Company", self.company, 'cost_center')
-
for d in item_dict:
stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
@@ -1197,9 +1207,10 @@ class StockEntry(StockController):
se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
se_child.stock_uom = stock_uom
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
- se_child.cost_center = item_dict[d].get("cost_center") or cost_center
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
se_child.subcontracted_item = item_dict[d].get("main_item_code")
+ se_child.cost_center = (item_dict[d].get("cost_center") or
+ get_default_cost_center(item_dict[d], company = self.company))
for field in ["idx", "po_detail", "original_item",
"expense_account", "description", "item_name"]:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index b1e38b340c5..48b4dc858c4 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -527,23 +527,40 @@ def get_default_deferred_account(args, item, fieldname=None):
else:
return None
-def get_default_cost_center(args, item, item_group, brand, company=None):
+def get_default_cost_center(args, item=None, item_group=None, brand=None, company=None):
cost_center = None
+
+ if not company and args.get("company"):
+ company = args.get("company")
+
if args.get('project'):
cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True)
- if not cost_center:
+ if not cost_center and (item and item_group and brand):
if args.get('customer'):
cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center')
else:
cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center')
- cost_center = cost_center or args.get("cost_center")
+ elif not cost_center and args.get("item_code") and company:
+ for method in ["get_item_defaults", "get_item_group_defaults", "get_brand_defaults"]:
+ path = "erpnext.stock.get_item_details.{0}".format(method)
+ data = frappe.get_attr(path)(args.get("item_code"), company)
+
+ if data and (data.selling_cost_center or data.buying_cost_center):
+ return data.selling_cost_center or data.buying_cost_center
+
+ if not cost_center and args.get("cost_center"):
+ cost_center = args.get("cost_center")
if (company and cost_center
and frappe.get_cached_value("Cost Center", cost_center, "company") != company):
return None
+ if not cost_center and company:
+ cost_center = frappe.get_cached_value("Company",
+ company, "cost_center")
+
return cost_center
def get_default_supplier(args, item, item_group, brand):
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index db39bae8a63..da4b529b01e 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -284,7 +284,6 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
return
convertible_cols = {}
-
is_dict_obj = False
if isinstance(result[0], dict):
is_dict_obj = True
@@ -306,13 +305,13 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
for row_idx, row in enumerate(result):
data = row.items() if is_dict_obj else enumerate(row)
for key, value in data:
- if not key in convertible_columns or not conversion_factors[row_idx]:
+ if key not in convertible_columns or not conversion_factors[row_idx-1]:
continue
if convertible_columns.get(key) == 'rate':
- new_value = flt(value) * conversion_factors[row_idx]
+ new_value = flt(value) * conversion_factors[row_idx-1]
else:
- new_value = flt(value) / conversion_factors[row_idx]
+ new_value = flt(value) / conversion_factors[row_idx-1]
if not is_dict_obj:
row.insert(key+1, new_value)
diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html
index 7ce3521273f..7a405d80cfe 100644
--- a/erpnext/www/lms/index.html
+++ b/erpnext/www/lms/index.html
@@ -45,7 +45,7 @@
{{ education_settings.description }}
{% if frappe.session.user == 'Guest' %}
- {{_('Sign Up')}}
+ {{_('Sign Up')}}
{% endif %}