Merge branch 'develop' into rfq-multi-uom

This commit is contained in:
rohitwaghchaure 2020-06-18 15:49:53 +05:30 committed by GitHub
commit 853fce8c7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 232 additions and 54 deletions

View File

@ -1376,6 +1376,12 @@
"read": 1,
"role": "Purchase Manager",
"write": 1
},
{
"email": 1,
"print": 1,
"read": 1,
"role": "Accounts User"
}
],
"search_fields": "status, transaction_date, supplier,grand_total",

View File

@ -118,7 +118,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
def test_add_new_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
@ -144,7 +144,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 2)
self.assertEqual(po.status, 'To Receive and Bill')
def test_remove_item_in_update_child_qty_rate(self):
po = create_purchase_order(do_not_save=1)
po.items[0].qty = 4
@ -185,6 +185,23 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 1)
self.assertEqual(po.status, 'To Receive and Bill')
def test_update_child_qty_rate_perm(self):
po = create_purchase_order(item_code= "_Test Item", qty=4)
user = 'test@example.com'
test_user = frappe.get_doc('User', user)
test_user.add_roles("Accounts User")
frappe.set_user(user)
# update qty
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
# add new item
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
frappe.set_user("Administrator")
def test_update_qty(self):
po = create_purchase_order()
@ -689,7 +706,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.save()
self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
def test_po_optional_blanket_order(self):
"""
Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.

View File

@ -1137,8 +1137,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
child_item.item_name = item.item_name
child_item.description = item.description
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
if not child_item.warehouse:
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
@ -1157,8 +1157,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
child_item.item_name = item.item_name
child_item.description = item.description
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
child_item.base_rate = 1 # Initiallize value will update in parent validation
child_item.base_amount = 1 # Initiallize value will update in parent validation
return child_item
@ -1190,6 +1190,26 @@ def check_and_delete_children(parent, data):
@frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
def check_permissions(doc, perm_type='create'):
try:
doc.check_permission(perm_type)
except:
action = "add" if perm_type == 'create' else "update"
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
def get_new_child_item(item_row):
if parent_doctype == "Sales Order":
return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
if parent_doctype == "Purchase Order":
return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
def validate_quantity(child_item, d):
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
frappe.throw(_("Cannot set quantity less than delivered quantity"))
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
frappe.throw(_("Cannot set quantity less than received quantity"))
data = json.loads(trans_items)
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
@ -1201,20 +1221,29 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
new_child_flag = False
if not d.get("docname"):
new_child_flag = True
if parent_doctype == "Sales Order":
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
if parent_doctype == "Purchase Order":
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
check_permissions(parent, 'create')
child_item = get_new_child_item(d)
else:
check_permissions(parent, 'write')
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
if parent_doctype == 'Sales Order':
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
elif parent_doctype == 'Purchase Order':
prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date")
rate_unchanged = prev_rate == new_rate
qty_unchanged = prev_qty == new_qty
conversion_factor_unchanged = prev_con_fac == new_con_fac
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
continue
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
frappe.throw(_("Cannot set quantity less than delivered quantity"))
if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty):
frappe.throw(_("Cannot set quantity less than received quantity"))
validate_quantity(child_item, d)
child_item.qty = flt(d.get("qty"))
precision = child_item.precision("rate") or 2
@ -1225,6 +1254,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
else:
child_item.rate = flt(d.get("rate"))
if d.get("conversion_factor"):
if child_item.stock_uom == child_item.uom:
child_item.conversion_factor = 1
else:
child_item.conversion_factor = flt(d.get('conversion_factor'))
if d.get("delivery_date") and parent_doctype == 'Sales Order':
child_item.delivery_date = d.get('delivery_date')
if d.get("schedule_date") and parent_doctype == 'Purchase Order':
child_item.schedule_date = d.get('schedule_date')
if flt(child_item.price_list_rate):
if flt(child_item.rate) > flt(child_item.price_list_rate):
# if rate is greater than price_list_rate, set margin

View File

@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name
if customer_exists:
frappe.rename_doc("Customer", old_name, customer_name)
billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"})
shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"})
rename_address(billing_address, customer)
rename_address(shipping_address, customer)
for address_type in ("Billing", "Shipping",):
try:
address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
rename_address(address, customer)
except (
frappe.DoesNotExistError,
frappe.DuplicateEntryError,
frappe.ValidationError,
):
pass
else:
create_address(raw_billing_data, customer, "Billing")
create_address(raw_shipping_data, customer, "Shipping")

View File

@ -238,6 +238,9 @@ doc_events = {
"on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel",
"on_trash": "erpnext.regional.check_deletion_permission"
},
"Purchase Invoice": {
"on_submit": "erpnext.regional.india.utils.make_reverse_charge_entries"
},
"Payment Entry": {
"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"],
"on_trash": "erpnext.regional.check_deletion_permission"

View File

@ -700,3 +700,4 @@ erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v13_0.update_sla_enhancements
erpnext.patches.v12_0.update_address_template_for_india
erpnext.patches.v12_0.set_multi_uom_in_rfq
erpnext.patches.v13_0.update_issue_metrics

View File

@ -0,0 +1,33 @@
from __future__ import unicode_literals
import frappe
from frappe.core.doctype.communication.communication import set_avg_response_time
from erpnext.support.doctype.issue.issue import set_resolution_time, set_user_resolution_time
def execute():
if frappe.db.exists('DocType', 'Issue'):
frappe.reload_doctype('Issue')
count = 0
for parent in frappe.get_all('Issue', order_by='creation desc'):
parent_doc = frappe.get_doc('Issue', parent.name)
communication = frappe.get_all('Communication', filters={
'reference_doctype': 'Issue',
'reference_name': parent.name,
'communication_medium': 'Email',
'sent_or_received': 'Sent'
}, order_by = 'creation asc', limit=1)
if communication:
communication_doc = frappe.get_doc('Communication', communication[0].name)
set_avg_response_time(parent_doc, communication_doc)
if parent_doc.status in ['Closed', 'Resolved']:
set_resolution_time(parent_doc)
set_user_resolution_time(parent_doc)
# commit after every 100 records
count += 1
if count % 100 == 0:
frappe.db.commit()

View File

@ -487,7 +487,14 @@ erpnext.utils.update_child_items = function(opts) {
fieldtype: 'Date',
fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
in_list_view: 1,
label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date")
label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"),
reqd: 1
})
fields.splice(3, 0, {
fieldtype: 'Float',
fieldname: "conversion_factor",
in_list_view: 1,
label: __("Conversion Factor")
})
}
@ -536,6 +543,7 @@ erpnext.utils.update_child_items = function(opts) {
"item_code": d.item_code,
"delivery_date": d.delivery_date,
"schedule_date": d.schedule_date,
"conversion_factor": d.conversion_factor,
"qty": d.qty,
"rate": d.rate,
});

View File

@ -52,7 +52,7 @@
<td class="disabled"></td>
<td class="disabled"></td>
<tr>
<td>(d) {{__("Inward Supplies(liable to reverse charge")}}</td>
<td>(d) {{__("Inward Supplies(liable to reverse charge)")}}</td>
<td class="right">{{ flt(data.sup_details.isup_rev.txval, 2) }}</td>
<td class="right">{{ flt(data.sup_details.isup_rev.iamt, 2) }}</td>
<td class="right">{{ flt(data.sup_details.isup_rev.camt, 2) }}</td>

View File

@ -158,7 +158,7 @@ class GSTR3BReport(Document):
self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"])
self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"])
self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Registered Regular"], reverse_charge="Y")
self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered", "Overseas"], reverse_charge="Y")
self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2)
self.set_itc_details(itc_details)
@ -192,31 +192,27 @@ class GSTR3BReport(Document):
for d in self.report_dict["itc_elg"]["itc_avl"]:
itc_type = itc_type_map.get(d["ty"])
gst_category = "Registered Regular"
gst_category = ["Registered Regular"]
if d["ty"] == 'ISRC':
reverse_charge = "Y"
itc_type = 'All Other ITC'
gst_category = ['Unregistered', 'Overseas']
else:
reverse_charge = "N"
for account_head in self.account_heads:
for category in gst_category:
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2)
d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2)
d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2)
d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2)
d["csamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2)
net_itc["iamt"] += flt(d["iamt"], 2)
net_itc["camt"] += flt(d["camt"], 2)
net_itc["samt"] += flt(d["samt"], 2)
net_itc["csamt"] += flt(d["csamt"], 2)
for key in ['iamt', 'camt', 'samt', 'csamt']:
net_itc[key] += flt(d[key], 2)
for account_head in self.account_heads:
itc_inelg = self.report_dict["itc_elg"]["itc_inelg"][1]
itc_inelg["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2)
itc_inelg["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2)
itc_inelg["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2)
itc_inelg["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2)
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
itc_inelg[key[0]] = flt(itc_details.get(("Ineligible", "N", account_head.get(key[1])), {}).get("amount"), 2)
def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"):
@ -274,17 +270,16 @@ class GSTR3BReport(Document):
""" #nosec
.format(doctype = doctype), (self.month_no, self.year, reverse_charge, self.company, self.gst_details.get("gstin"))))
def get_itc_details(self, reverse_charge='N'):
def get_itc_details(self):
itc_amount = frappe.db.sql("""
select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
where s.docstatus = 1 and t.parent = s.name
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
and s.company_gstin = %s
group by t.account_head, s.gst_category, s.eligibility_for_itc
""",
(reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
(self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
itc_details = {}

View File

@ -9,6 +9,8 @@ from erpnext.hr.utils import get_salary_assignment
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.regional.india import number_state_mapping
from six import string_types
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.utils import get_account_currency
def validate_gstin_for_india(doc, method):
if hasattr(doc, 'gst_state') and doc.gst_state:
@ -658,5 +660,53 @@ def get_gst_accounts(company, account_wise=False):
elif val:
gst_accounts[val] = acc
return gst_accounts
def make_reverse_charge_entries(doc, method):
country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'India':
return
if doc.reverse_charge == 'Y':
gl_entries = []
gst_accounts = get_gst_accounts(doc.company)
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+ gst_accounts.get('igst_account')
for tax in doc.get('taxes'):
if tax.category not in ("Total", "Valuation and Total"):
continue
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
account_currency = get_account_currency(tax.account_head)
gl_entries.append(doc.get_gl_dict(
{
"account": tax.account_head,
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
"against": doc.supplier,
"credit": tax.base_tax_amount_after_discount_amount,
"credits_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==doc.company_currency \
else tax.tax_amount_after_discount_amount
}, account_currency, item=tax)
)
gl_entries.append(doc.get_gl_dict(
{
"account": doc.credit_to if doc.doctype == 'Purchase Invoice' else doc.debit_to,
"cost_center": doc.cost_center,
"posting_date": doc.posting_date,
"party_type": 'Supplier',
"party": doc.supplier,
"against": tax.account_head,
"debit": tax.base_tax_amount_after_discount_amount,
"debit_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==doc.company_currency \
else tax.tax_amount_after_discount_amount
}, account_currency, item=doc)
)
make_gl_entries(gl_entries)

View File

@ -400,6 +400,23 @@ class TestSalesOrder(unittest.TestCase):
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
def test_update_child_qty_rate_perm(self):
so = make_sales_order(item_code= "_Test Item", qty=4)
user = 'test@example.com'
test_user = frappe.get_doc('User', user)
test_user.add_roles("Accounts User")
frappe.set_user(user)
# update qty
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
# add new item
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
frappe.set_user("Administrator")
def test_warehouse_user(self):
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")

View File

@ -769,22 +769,23 @@
"label": "Against Blanket Order"
},
{
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"no_copy": 1,
"options": "BOM",
"print_hide": 1
},
{
"fieldname": "manufacturing_section_section",
"fieldtype": "Section Break",
"label": "Manufacturing Section"
}
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"no_copy": 1,
"options": "BOM",
"print_hide": 1
},
{
"fieldname": "manufacturing_section_section",
"fieldtype": "Section Break",
"label": "Manufacturing Section"
}
],
"idx": 1,
"istable": 1,
"modified": "2020-05-15 18:13:43.006493",
"links": [],
"modified": "2020-05-29 20:54:32.309460",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",