Merge branch 'develop' of https://github.com/frappe/erpnext into develop
This commit is contained in:
commit
3a196b09ee
@ -11,6 +11,7 @@ class ModeofPayment(Document):
|
||||
def validate(self):
|
||||
self.validate_accounts()
|
||||
self.validate_repeating_companies()
|
||||
self.validate_pos_mode_of_payment()
|
||||
|
||||
def validate_repeating_companies(self):
|
||||
"""Error when Same Company is entered multiple times in accounts"""
|
||||
@ -27,3 +28,15 @@ class ModeofPayment(Document):
|
||||
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
|
||||
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
|
||||
.format(entry.default_account, entry.company, self.name))
|
||||
|
||||
def validate_pos_mode_of_payment(self):
|
||||
if not self.enabled:
|
||||
pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
|
||||
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
|
||||
pos_profiles = list(map(lambda x: x[0], pos_profiles))
|
||||
|
||||
if pos_profiles:
|
||||
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
||||
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
||||
frappe.throw(_(message), title="Not Allowed")
|
||||
|
||||
|
@ -149,6 +149,7 @@
|
||||
"column_break_63",
|
||||
"status",
|
||||
"inter_company_invoice_reference",
|
||||
"is_internal_supplier",
|
||||
"remarks",
|
||||
"subscription_section",
|
||||
"from_date",
|
||||
@ -418,7 +419,6 @@
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Contact Email",
|
||||
"options": "Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1284,6 +1284,14 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "supplier.is_internal_supplier",
|
||||
"fieldname": "is_internal_supplier",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Supplier",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
|
@ -224,7 +224,7 @@ class PurchaseInvoice(BuyingController):
|
||||
for item in self.get("items"):
|
||||
# in case of auto inventory accounting,
|
||||
# expense account is always "Stock Received But Not Billed" for a stock item
|
||||
# except epening entry, drop-ship entry and fixed asset items
|
||||
# except opening entry, drop-ship entry and fixed asset items
|
||||
if item.item_code:
|
||||
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||
|
||||
@ -233,10 +233,22 @@ class PurchaseInvoice(BuyingController):
|
||||
and (not item.po_detail or
|
||||
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
|
||||
|
||||
if self.update_stock:
|
||||
if self.update_stock and (not item.from_warehouse):
|
||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||
else:
|
||||
item.expense_account = stock_not_billed_account
|
||||
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
|
||||
if item.purchase_receipt:
|
||||
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no=%s and account = %s""",
|
||||
(item.purchase_receipt, stock_not_billed_account))
|
||||
|
||||
if negative_expense_booked_in_pr:
|
||||
item.expense_account = stock_not_billed_account
|
||||
else:
|
||||
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
||||
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
||||
item.expense_account = stock_not_billed_account
|
||||
|
||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||
company = self.company)
|
||||
@ -467,16 +479,47 @@ class PurchaseInvoice(BuyingController):
|
||||
warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
|
||||
item, voucher_wise_stock_value, account_currency)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
if item.from_warehouse:
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[item.warehouse]['account'],
|
||||
"against": warehouse_account[item.from_warehouse]["account"],
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": warehouse_debit_amount,
|
||||
}, warehouse_account[item.warehouse]["account_currency"], item=item))
|
||||
|
||||
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[item.from_warehouse]['account'],
|
||||
"against": warehouse_account[item.warehouse]["account"],
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
else:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
# Amount added through landed-cost-voucher
|
||||
if landed_cost_entries:
|
||||
|
@ -63,6 +63,7 @@
|
||||
"warehouse_section",
|
||||
"warehouse",
|
||||
"rejected_warehouse",
|
||||
"from_warehouse",
|
||||
"quality_inspection",
|
||||
"batch_no",
|
||||
"col_br_wh",
|
||||
@ -762,16 +763,22 @@
|
||||
"fetch_from": "item_code.asset_category",
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Data",
|
||||
"in_preview": 1,
|
||||
"label": "Asset Category",
|
||||
"options": "Asset Category",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Supplier Warehouse",
|
||||
"options": "Warehouse"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-04 12:23:17.046413",
|
||||
"modified": "2020-01-13 16:04:14.200462",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
@ -152,8 +152,11 @@ def update_multi_mode_option(doc, pos_profile):
|
||||
|
||||
|
||||
def get_mode_of_payment(doc):
|
||||
return frappe.db.sql(""" select mpa.default_account, mpa.parent, mp.type as type from `tabMode of Payment Account` mpa, \
|
||||
`tabMode of Payment` mp where mpa.parent = mp.name and mpa.company = %(company)s""", {'company': doc.company}, as_dict=1)
|
||||
return frappe.db.sql("""
|
||||
select mpa.default_account, mpa.parent, mp.type as type
|
||||
from `tabMode of Payment Account` mpa,`tabMode of Payment` mp
|
||||
where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
|
||||
{'company': doc.company}, as_dict=1)
|
||||
|
||||
|
||||
def update_tax_table(doc):
|
||||
|
@ -153,6 +153,7 @@
|
||||
"select_print_heading",
|
||||
"more_information",
|
||||
"inter_company_invoice_reference",
|
||||
"is_internal_customer",
|
||||
"customer_group",
|
||||
"campaign",
|
||||
"is_discounted",
|
||||
@ -1563,6 +1564,14 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "customer.is_internal_customer",
|
||||
"fieldname": "is_internal_customer",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Customer",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
|
@ -1426,23 +1426,42 @@ def set_account_for_mode_of_payment(self):
|
||||
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
|
||||
|
||||
def get_inter_company_details(doc, doctype):
|
||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||
party = frappe.db.get_value("Supplier", {"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}, "name")
|
||||
if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]:
|
||||
parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company})
|
||||
company = frappe.get_cached_value("Customer", doc.customer, "represents_company")
|
||||
|
||||
party = get_internal_party(parties, "Supplier", doc)
|
||||
else:
|
||||
party = frappe.db.get_value("Customer", {"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}, "name")
|
||||
parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company})
|
||||
company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company")
|
||||
|
||||
party = get_internal_party(parties, "Customer", doc)
|
||||
|
||||
return {
|
||||
"party": party,
|
||||
"company": company
|
||||
}
|
||||
|
||||
def get_internal_party(parties, link_doctype, doc):
|
||||
if len(parties) == 1:
|
||||
party = parties[0].name
|
||||
else:
|
||||
# If more than one Internal Supplier/Customer, get supplier/customer on basis of address
|
||||
if doc.get('company_address') or doc.get('shipping_address'):
|
||||
party = frappe.db.get_value("Dynamic Link", {"parent": doc.get('company_address') or doc.get('shipping_address'),
|
||||
"parenttype": "Address", "link_doctype": link_doctype}, "link_name")
|
||||
|
||||
if not party:
|
||||
party = parties[0].name
|
||||
else:
|
||||
party = parties[0].name
|
||||
|
||||
return party
|
||||
|
||||
def validate_inter_company_transaction(doc, doctype):
|
||||
|
||||
details = get_inter_company_details(doc, doctype)
|
||||
price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order"] else doc.buying_price_list
|
||||
price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list
|
||||
valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
|
||||
if not valid_price_list:
|
||||
frappe.throw(_("Selected Price List should have buying and selling fields checked."))
|
||||
|
@ -35,8 +35,7 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
||||
|
||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
||||
fetch_payment_terms_template=True, party_address=None, company_address=None,shipping_address=None, pos_profile=None):
|
||||
|
||||
fetch_payment_terms_template=True, party_address=None, company_address=None, shipping_address=None, pos_profile=None):
|
||||
party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||
party = party_details[party_type.lower()]
|
||||
|
||||
|
@ -100,6 +100,11 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "group_by_party",
|
||||
"label": __("Group By Supplier"),
|
||||
"fieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"fieldname":"based_on_payment_terms",
|
||||
"label": __("Based On Payment Terms"),
|
||||
@ -112,6 +117,16 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"hidden": 1
|
||||
}
|
||||
],
|
||||
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (data && data.bold) {
|
||||
value = value.bold();
|
||||
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
onload: function(report) {
|
||||
report.page.add_inner_button(__("Accounts Payable Summary"), function() {
|
||||
var filters = report.get_values();
|
||||
|
@ -87,7 +87,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
||||
});
|
||||
|
||||
frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
|
||||
frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
|
||||
["credit_limit"], function(value) {
|
||||
if (value) {
|
||||
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
||||
@ -131,6 +131,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Person"
|
||||
},
|
||||
{
|
||||
"fieldname": "group_by_party",
|
||||
"label": __("Group By Customer"),
|
||||
"fieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"fieldname":"based_on_payment_terms",
|
||||
"label": __("Based On Payment Terms"),
|
||||
@ -177,6 +182,15 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
}
|
||||
],
|
||||
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (data && data.bold) {
|
||||
value = value.bold();
|
||||
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
onload: function(report) {
|
||||
report.page.add_inner_button(__("Accounts Receivable Summary"), function() {
|
||||
var filters = report.get_values();
|
||||
|
@ -46,7 +46,7 @@ class ReceivablePayableReport(object):
|
||||
self.get_columns()
|
||||
self.get_data()
|
||||
self.get_chart_data()
|
||||
return self.columns, self.data, None, self.chart
|
||||
return self.columns, self.data, None, self.chart, None, self.skip_total_row
|
||||
|
||||
def set_defaults(self):
|
||||
if not self.filters.get("company"):
|
||||
@ -57,6 +57,12 @@ class ReceivablePayableReport(object):
|
||||
self.party_type = self.filters.party_type
|
||||
self.party_details = {}
|
||||
self.invoices = set()
|
||||
self.skip_total_row = 0
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.previous_party=''
|
||||
self.total_row_map = {}
|
||||
self.skip_total_row = 1
|
||||
|
||||
def get_data(self):
|
||||
self.get_gl_entries()
|
||||
@ -102,6 +108,12 @@ class ReceivablePayableReport(object):
|
||||
)
|
||||
self.get_invoices(gle)
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.init_subtotal_row(gle.party)
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.init_subtotal_row('Total')
|
||||
|
||||
def get_invoices(self, gle):
|
||||
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
||||
if self.filters.get("sales_person"):
|
||||
@ -111,6 +123,20 @@ class ReceivablePayableReport(object):
|
||||
else:
|
||||
self.invoices.add(gle.voucher_no)
|
||||
|
||||
def init_subtotal_row(self, party):
|
||||
if not self.total_row_map.get(party):
|
||||
self.total_row_map.setdefault(party, {
|
||||
'party': party,
|
||||
'bold': 1
|
||||
})
|
||||
|
||||
for field in self.get_currency_fields():
|
||||
self.total_row_map[party][field] = 0.0
|
||||
|
||||
def get_currency_fields(self):
|
||||
return ['invoiced', 'paid', 'credit_note', 'outstanding', 'range1',
|
||||
'range2', 'range3', 'range4', 'range5']
|
||||
|
||||
def update_voucher_balance(self, gle):
|
||||
# get the row where this balance needs to be updated
|
||||
# if its a payment, it will return the linked invoice or will be considered as advance
|
||||
@ -135,6 +161,18 @@ class ReceivablePayableReport(object):
|
||||
# advance / unlinked payment or other adjustment
|
||||
row.paid -= gle_balance
|
||||
|
||||
def update_sub_total_row(self, row, party):
|
||||
total_row = self.total_row_map.get(party)
|
||||
|
||||
for field in self.get_currency_fields():
|
||||
total_row[field] += row.get(field, 0.0)
|
||||
|
||||
def append_subtotal_row(self, party):
|
||||
sub_total_row = self.total_row_map.get(party)
|
||||
self.data.append(sub_total_row)
|
||||
self.data.append({})
|
||||
self.update_sub_total_row(sub_total_row, 'Total')
|
||||
|
||||
def get_voucher_balance(self, gle):
|
||||
if self.filters.get("sales_person"):
|
||||
against_voucher = gle.against_voucher or gle.voucher_no
|
||||
@ -192,11 +230,22 @@ class ReceivablePayableReport(object):
|
||||
else:
|
||||
self.append_row(row)
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.append_subtotal_row(self.previous_party)
|
||||
self.data.append(self.total_row_map.get('Total'))
|
||||
|
||||
def append_row(self, row):
|
||||
self.allocate_future_payments(row)
|
||||
self.set_invoice_details(row)
|
||||
self.set_party_details(row)
|
||||
self.set_ageing(row)
|
||||
|
||||
if self.filters.get('group_by_party'):
|
||||
self.update_sub_total_row(row, row.party)
|
||||
if self.previous_party and (self.previous_party != row.party):
|
||||
self.append_subtotal_row(self.previous_party)
|
||||
self.previous_party = row.party
|
||||
|
||||
self.data.append(row)
|
||||
|
||||
def set_invoice_details(self, row):
|
||||
@ -503,6 +552,7 @@ class ReceivablePayableReport(object):
|
||||
# get all the GL entries filtered by the given filters
|
||||
|
||||
conditions, values = self.prepare_conditions()
|
||||
order_by = self.get_order_by_condition()
|
||||
|
||||
if self.filters.get(scrub(self.party_type)):
|
||||
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
||||
@ -520,9 +570,8 @@ class ReceivablePayableReport(object):
|
||||
and party_type=%s
|
||||
and (party is not null and party != '')
|
||||
and posting_date <= %s
|
||||
{1}
|
||||
order by posting_date, party"""
|
||||
.format(select_fields, conditions), values, as_dict=True)
|
||||
{1} {2}"""
|
||||
.format(select_fields, conditions, order_by), values, as_dict=True)
|
||||
|
||||
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||
if self.filters.get("sales_person"):
|
||||
@ -557,6 +606,12 @@ class ReceivablePayableReport(object):
|
||||
|
||||
return " and ".join(conditions), values
|
||||
|
||||
def get_order_by_condition(self):
|
||||
if self.filters.get('group_by_party'):
|
||||
return "order by party, posting_date"
|
||||
else:
|
||||
return "order by posting_date, party"
|
||||
|
||||
def add_common_filters(self, conditions, values, party_type_field):
|
||||
if self.filters.company:
|
||||
conditions.append("company=%s")
|
||||
@ -736,11 +791,13 @@ class ReceivablePayableReport(object):
|
||||
def get_chart_data(self):
|
||||
rows = []
|
||||
for row in self.data:
|
||||
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
||||
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
rows.append({
|
||||
'values': [flt(val, precision) for val in values]
|
||||
})
|
||||
row = frappe._dict(row)
|
||||
if not cint(row.bold):
|
||||
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
||||
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
rows.append({
|
||||
'values': [flt(val, precision) for val in values]
|
||||
})
|
||||
|
||||
self.chart = {
|
||||
"data": {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,6 +43,7 @@ class BuyingController(StockController):
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.validate_warehouse()
|
||||
self.validate_from_warehouse()
|
||||
self.set_supplier_address()
|
||||
|
||||
if self.doctype=="Purchase Invoice":
|
||||
@ -115,6 +116,14 @@ class BuyingController(StockController):
|
||||
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
|
||||
d.db_set('cost_center', lc_voucher_data[0][1])
|
||||
|
||||
def validate_from_warehouse(self):
|
||||
for item in self.get('items'):
|
||||
if item.get('from_warehouse') and (item.get('from_warehouse') == item.get('warehouse')):
|
||||
frappe.throw(_("Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same").format(item.idx))
|
||||
|
||||
if item.get('from_warehouse') and self.get('is_subcontracted') == 'Yes':
|
||||
frappe.throw(_("Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor").format(item.idx))
|
||||
|
||||
def set_supplier_address(self):
|
||||
address_dict = {
|
||||
'supplier_address': 'address_display',
|
||||
@ -521,6 +530,16 @@ class BuyingController(StockController):
|
||||
pr_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||
|
||||
if pr_qty:
|
||||
|
||||
if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==1)
|
||||
or (cint(self.is_return) and self.docstatus==2)):
|
||||
from_warehouse_sle = self.get_sl_entries(d, {
|
||||
"actual_qty": -1 * pr_qty,
|
||||
"warehouse": d.from_warehouse
|
||||
})
|
||||
|
||||
sl_entries.append(from_warehouse_sle)
|
||||
|
||||
sle = self.get_sl_entries(d, {
|
||||
"actual_qty": flt(pr_qty),
|
||||
"serial_no": cstr(d.serial_no).strip()
|
||||
@ -541,6 +560,15 @@ class BuyingController(StockController):
|
||||
})
|
||||
sl_entries.append(sle)
|
||||
|
||||
if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==2)
|
||||
or (cint(self.is_return) and self.docstatus==1)):
|
||||
from_warehouse_sle = self.get_sl_entries(d, {
|
||||
"actual_qty": -1 * pr_qty,
|
||||
"warehouse": d.from_warehouse
|
||||
})
|
||||
|
||||
sl_entries.append(from_warehouse_sle)
|
||||
|
||||
if flt(d.rejected_qty) != 0:
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
"warehouse": d.rejected_warehouse,
|
||||
|
@ -180,7 +180,7 @@ class SellingController(StockController):
|
||||
|
||||
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
||||
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
||||
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom) and not self.get('is_internal_customer'):
|
||||
throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
|
||||
|
||||
last_valuation_rate = frappe.db.sql("""
|
||||
@ -190,7 +190,8 @@ class SellingController(StockController):
|
||||
""", (it.item_code, it.warehouse))
|
||||
if last_valuation_rate:
|
||||
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
|
||||
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom):
|
||||
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom) \
|
||||
and not self.get('is_internal_customer'):
|
||||
throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate")
|
||||
|
||||
|
||||
@ -300,7 +301,7 @@ class SellingController(StockController):
|
||||
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
|
||||
return_rate = 0
|
||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
||||
return_rate = self.get_incoming_rate_for_return(d.item_code, self.return_against)
|
||||
|
||||
# On cancellation or if return entry submission, make stock ledger entry for
|
||||
# target warehouse first, to update serial no values properly
|
||||
|
@ -72,7 +72,7 @@ class StockController(AccountsController):
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
if warehouse_account.get(sle.warehouse):
|
||||
# from warehouse account
|
||||
# from warehouse account/ target warehouse account
|
||||
|
||||
self.check_expense_account(item_row)
|
||||
|
||||
@ -96,7 +96,7 @@ class StockController(AccountsController):
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
|
||||
|
||||
# to target warehouse / expense account
|
||||
# expense account
|
||||
gl_list.append(self.get_gl_dict({
|
||||
"account": item_row.expense_account,
|
||||
"against": warehouse_account[sle.warehouse]["account"],
|
||||
@ -288,7 +288,7 @@ class StockController(AccountsController):
|
||||
|
||||
return serialized_items
|
||||
|
||||
def get_incoming_rate_for_sales_return(self, item_code, against_document):
|
||||
def get_incoming_rate_for_return(self, item_code, against_document):
|
||||
incoming_rate = 0.0
|
||||
if against_document and item_code:
|
||||
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
|
||||
@ -306,6 +306,16 @@ class StockController(AccountsController):
|
||||
warehouses = list(set([d.warehouse for d in
|
||||
self.get("items") if getattr(d, "warehouse", None)]))
|
||||
|
||||
target_warehouses = list(set([d.target_warehouse for d in
|
||||
self.get("items") if getattr(d, "target_warehouse", None)]))
|
||||
|
||||
warehouses.extend(target_warehouses)
|
||||
|
||||
from_warehouse = list(set([d.from_warehouse for d in
|
||||
self.get("items") if getattr(d, "from_warehouse", None)]))
|
||||
|
||||
warehouses.extend(from_warehouse)
|
||||
|
||||
for w in warehouses:
|
||||
validate_warehouse_company(w, self.company)
|
||||
|
||||
|
@ -29,10 +29,15 @@ class StudentApplicant(Document):
|
||||
set_name_by_naming_series(self)
|
||||
|
||||
def validate(self):
|
||||
self.validate_dates()
|
||||
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
||||
if self.student_admission and self.program and self.date_of_birth:
|
||||
self.validation_from_student_admission()
|
||||
|
||||
def validate_dates(self):
|
||||
if self.date_of_birth and getdate(self.date_of_birth) >= getdate():
|
||||
frappe.throw(_("Date of Birth cannot be greater than today."))
|
||||
|
||||
def on_update_after_submit(self):
|
||||
student = frappe.get_list("Student", filters= {"student_applicant": self.name})
|
||||
if student:
|
||||
|
@ -2,6 +2,11 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Department', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("parent_department", function(){
|
||||
return {"filters": [["Department", "is_group", "=", 1]]};
|
||||
});
|
||||
},
|
||||
refresh: function(frm) {
|
||||
// read-only for root department
|
||||
if(!frm.doc.parent_department && !frm.is_new()) {
|
||||
|
@ -652,6 +652,7 @@ erpnext.patches.v12_0.set_production_capacity_in_workstation
|
||||
erpnext.patches.v12_0.set_employee_preferred_emails
|
||||
erpnext.patches.v12_0.set_against_blanket_order_in_sales_and_purchase_order
|
||||
erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim
|
||||
erpnext.patches.v12_0.add_eway_bill_in_delivery_note
|
||||
erpnext.patches.v12_0.set_lead_title_field
|
||||
erpnext.patches.v12_0.set_permission_einvoicing
|
||||
erpnext.patches.v12_0.set_published_in_hub_tracked_item
|
||||
|
19
erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py
Normal file
19
erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py
Normal file
@ -0,0 +1,19 @@
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
|
||||
if not company:
|
||||
return
|
||||
|
||||
create_custom_field('Delivery Note', {
|
||||
'fieldname': 'ewaybill',
|
||||
'label': 'E-Way Bill No.',
|
||||
'fieldtype': 'Data',
|
||||
'depends_on': 'eval:(doc.docstatus === 1)',
|
||||
'allow_on_submit': 1,
|
||||
'insert_after': 'customer_name_in_arabic',
|
||||
'translatable': 0,
|
||||
'owner': 'Administrator'
|
||||
})
|
@ -3,6 +3,7 @@
|
||||
|
||||
erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
setup: function() {},
|
||||
|
||||
apply_pricing_rule_on_item: function(item){
|
||||
let effective_item_rate = item.price_list_rate;
|
||||
if (item.parenttype === "Sales Order" && item.blanket_order_rate) {
|
||||
|
@ -490,7 +490,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
cost_center: item.cost_center,
|
||||
tax_category: me.frm.doc.tax_category,
|
||||
item_tax_template: item.item_tax_template,
|
||||
child_docname: item.name,
|
||||
child_docname: item.name
|
||||
}
|
||||
},
|
||||
|
||||
@ -504,7 +504,20 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
me.apply_product_discount(d.free_item_data);
|
||||
}
|
||||
},
|
||||
() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
|
||||
() => {
|
||||
// for internal customer instead of pricing rule directly apply valuation rate on item
|
||||
if (me.frm.doc.is_internal_customer || me.frm.doc.is_internal_supplier) {
|
||||
me.get_incoming_rate(item, me.frm.posting_date, me.frm.posting_time,
|
||||
me.frm.doc.doctype, me.frm.doc.company);
|
||||
} else {
|
||||
me.frm.script_manager.trigger("price_list_rate", cdt, cdn);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
if (me.frm.doc.is_internal_customer || me.frm.doc.is_internal_supplier) {
|
||||
me.calculate_taxes_and_totals();
|
||||
}
|
||||
},
|
||||
() => me.toggle_conversion_factor(item),
|
||||
() => {
|
||||
if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) {
|
||||
@ -528,6 +541,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
},
|
||||
|
||||
get_incoming_rate: function(item, posting_date, posting_time, voucher_type, company) {
|
||||
|
||||
let item_args = {
|
||||
'item_code': item.item_code,
|
||||
'warehouse': in_list('Purchase Receipt', 'Purchase Invoice') ? item.from_warehouse : item.warehouse,
|
||||
'posting_date': posting_date,
|
||||
'posting_time': posting_time,
|
||||
'qty': item.qty * item.conversion_factor,
|
||||
'serial_no': item.serial_no,
|
||||
'voucher_type': voucher_type,
|
||||
'company': company,
|
||||
'allow_zero_valuation_rate': item.allow_zero_valuation_rate
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.utils.get_incoming_rate',
|
||||
args: {
|
||||
args: item_args
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.model.set_value(item.doctype, item.name, 'rate', r.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||
let me = this;
|
||||
|
||||
@ -941,15 +979,19 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
|
||||
if(doc.doctype != 'Material Request' && frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
||||
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
|
||||
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
|
||||
item.total_weight = flt(item.stock_qty * item.weight_per_unit);
|
||||
refresh_field("stock_qty", item.name, item.parentfield);
|
||||
refresh_field("total_weight", item.name, item.parentfield);
|
||||
this.toggle_conversion_factor(item);
|
||||
this.calculate_net_weight();
|
||||
|
||||
if(doc.doctype != "Material Request") {
|
||||
item.total_weight = flt(item.stock_qty * item.weight_per_unit);
|
||||
refresh_field("total_weight", item.name, item.parentfield);
|
||||
this.calculate_net_weight();
|
||||
}
|
||||
|
||||
if (!dont_fetch_price_list_rate &&
|
||||
frappe.meta.has_field(doc.doctype, "price_list_currency")) {
|
||||
this.apply_price_list(item, true);
|
||||
|
@ -152,6 +152,7 @@ def create_purchase_invoices():
|
||||
currency = 'INR',
|
||||
warehouse = 'Finished Goods - _GST',
|
||||
cost_center = 'Main - _GST',
|
||||
expense_account = 'Cost of Goods Sold - _GST',
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
@ -181,6 +182,7 @@ def create_purchase_invoices():
|
||||
currency = 'INR',
|
||||
warehouse = 'Finished Goods - _GST',
|
||||
cost_center = 'Main - _GST',
|
||||
expense_account = 'Cost of Goods Sold - _GST',
|
||||
item = "Milk",
|
||||
do_not_save=1
|
||||
)
|
||||
|
@ -245,7 +245,16 @@ def make_custom_fields(update=True):
|
||||
'insert_after': 'lr_date',
|
||||
'print_hide': 1,
|
||||
'translatable': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
'fieldname': 'ewaybill',
|
||||
'label': 'E-Way Bill No.',
|
||||
'fieldtype': 'Data',
|
||||
'depends_on': 'eval:(doc.docstatus === 1)',
|
||||
'allow_on_submit': 1,
|
||||
'insert_after': 'customer_name_in_arabic',
|
||||
'translatable': 0,
|
||||
}
|
||||
]
|
||||
|
||||
si_ewaybill_fields = [
|
||||
@ -361,7 +370,7 @@ def make_custom_fields(update=True):
|
||||
},
|
||||
{
|
||||
'fieldname': 'ewaybill',
|
||||
'label': 'e-Way Bill No.',
|
||||
'label': 'E-Way Bill No.',
|
||||
'fieldtype': 'Data',
|
||||
'depends_on': 'eval:(doc.docstatus === 1)',
|
||||
'allow_on_submit': 1,
|
||||
|
@ -32,6 +32,9 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
|
||||
} else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
|
||||
frm.set_value('taxes_and_charges', '');
|
||||
frm.set_value('taxes', []);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -141,18 +141,24 @@ def get_place_of_supply(party_details, doctype):
|
||||
address_name = party_details.shipping_address or party_details.supplier_address
|
||||
|
||||
if address_name:
|
||||
address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number"], as_dict=1)
|
||||
address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number", "gstin"], as_dict=1)
|
||||
if address and address.gst_state and address.gst_state_number:
|
||||
party_details.gstin = address.gstin
|
||||
return cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_regional_address_details(party_details, doctype, company, return_taxes=None):
|
||||
|
||||
if isinstance(party_details, string_types):
|
||||
party_details = json.loads(party_details)
|
||||
party_details = frappe._dict(party_details)
|
||||
|
||||
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
||||
|
||||
if is_internal_transfer(party_details, doctype):
|
||||
party_details.taxes_and_charges = ''
|
||||
party_details.taxes = ''
|
||||
return
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||
master_doctype = "Sales Taxes and Charges Template"
|
||||
|
||||
@ -167,7 +173,6 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N
|
||||
|
||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||
master_doctype = "Purchase Taxes and Charges Template"
|
||||
|
||||
get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier')
|
||||
get_tax_template_based_on_category(master_doctype, company, party_details)
|
||||
|
||||
@ -196,6 +201,17 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N
|
||||
if return_taxes:
|
||||
return party_details
|
||||
|
||||
def is_internal_transfer(party_details, doctype):
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||
destination_gstin = party_details.company_gstin
|
||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||
destination_gstin = party_details.supplier_gstin
|
||||
|
||||
if party_details.gstin == destination_gstin:
|
||||
return True
|
||||
else:
|
||||
False
|
||||
|
||||
def get_tax_template_based_on_category(master_doctype, company, party_details):
|
||||
if not party_details.get('tax_category'):
|
||||
return
|
||||
@ -218,7 +234,6 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code):
|
||||
(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')
|
||||
|
||||
return default_tax
|
||||
|
||||
def get_tax_template_for_sez(party_details, master_doctype, company, party_type):
|
||||
@ -357,16 +372,13 @@ def calculate_hra_exemption_for_period(doc):
|
||||
return exemptions
|
||||
|
||||
def get_ewb_data(dt, dn):
|
||||
if dt != 'Sales Invoice':
|
||||
frappe.throw(_('E-Way Bill JSON can only be generated from Sales Invoice'))
|
||||
|
||||
dn = dn.split(',')
|
||||
|
||||
ewaybills = []
|
||||
for doc_name in dn:
|
||||
doc = frappe.get_doc(dt, doc_name)
|
||||
|
||||
validate_sales_invoice(doc)
|
||||
validate_doc(doc)
|
||||
|
||||
data = frappe._dict({
|
||||
"transporterId": "",
|
||||
@ -376,7 +388,9 @@ def get_ewb_data(dt, dn):
|
||||
data.userGstin = data.fromGstin = doc.company_gstin
|
||||
data.supplyType = 'O'
|
||||
|
||||
if doc.gst_category in ['Registered Regular', 'SEZ']:
|
||||
if dt == 'Delivery Note':
|
||||
data.subSupplyType = 1
|
||||
elif doc.gst_category in ['Registered Regular', 'SEZ']:
|
||||
data.subSupplyType = 1
|
||||
elif doc.gst_category in ['Overseas', 'Deemed Export']:
|
||||
data.subSupplyType = 3
|
||||
@ -535,7 +549,7 @@ def get_item_list(data, doc):
|
||||
|
||||
return data
|
||||
|
||||
def validate_sales_invoice(doc):
|
||||
def validate_doc(doc):
|
||||
if doc.docstatus != 1:
|
||||
frappe.throw(_('E-Way Bill JSON can only be generated from submitted document'))
|
||||
|
||||
|
@ -208,8 +208,7 @@
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"unique": 1
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"depends_on": "represents_company",
|
||||
|
@ -92,6 +92,15 @@ frappe.ui.form.on("Delivery Note", {
|
||||
}, __('Create'));
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) {
|
||||
frm.add_custom_button(__('Purchase Receipt'), function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
||||
frm: frm,
|
||||
})
|
||||
}, __('Create'));
|
||||
}
|
||||
},
|
||||
|
||||
to_warehouse: function(frm) {
|
||||
|
@ -115,6 +115,8 @@
|
||||
"campaign",
|
||||
"source",
|
||||
"column_break5",
|
||||
"is_internal_customer",
|
||||
"inter_company_reference",
|
||||
"per_billed",
|
||||
"customer_group",
|
||||
"territory",
|
||||
@ -1234,13 +1236,27 @@
|
||||
{
|
||||
"fieldname": "section_break_18",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "customer.is_internal_customer",
|
||||
"fieldname": "is_internal_customer",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Customer",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "inter_company_reference",
|
||||
"fieldtype": "Link",
|
||||
"label": "Inter Company Reference",
|
||||
"options": "Purchase Receipt"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-truck",
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-30 19:17:13.122644",
|
||||
"modified": "2019-12-31 19:17:13.122644",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -14,6 +14,7 @@ from frappe.desk.notifications import clear_doctype_notifications
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import cint, flt
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -587,3 +588,77 @@ def make_sales_return(source_name, target_doc=None):
|
||||
def update_delivery_note_status(docname, status):
|
||||
dn = frappe.get_doc("Delivery Note", docname)
|
||||
dn.update_status(status)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_inter_company_purchase_receipt(source_name, target_doc=None):
|
||||
return make_inter_company_transaction("Delivery Note", source_name, target_doc)
|
||||
|
||||
def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details
|
||||
|
||||
if doctype == 'Delivery Note':
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = "Purchase Receipt"
|
||||
source_document_warehouse_field = 'target_warehouse'
|
||||
target_document_warehouse_field = 'from_warehouse'
|
||||
else:
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = 'Delivery Note'
|
||||
source_document_warehouse_field = 'from_warehouse'
|
||||
target_document_warehouse_field = 'target_warehouse'
|
||||
|
||||
validate_inter_company_transaction(source_doc, doctype)
|
||||
details = get_inter_company_details(source_doc, doctype)
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.run_method("set_missing_values")
|
||||
|
||||
if target.doctype == 'Purchase Receipt':
|
||||
master_doctype = 'Purchase Taxes and Charges Template'
|
||||
else:
|
||||
master_doctype = 'Sales Taxes and Charges Template'
|
||||
|
||||
if not target.get('taxes') and target.get('taxes_and_charges'):
|
||||
for tax in get_taxes_and_charges(master_doctype, target.get('taxes_and_charges')):
|
||||
target.append('taxes', tax)
|
||||
|
||||
def update_details(source_doc, target_doc, source_parent):
|
||||
target_doc.inter_company_invoice_reference = source_doc.name
|
||||
if target_doc.doctype == 'Purchase Receipt':
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.supplier = details.get("party")
|
||||
target_doc.supplier_address = source_doc.company_address
|
||||
target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address
|
||||
target_doc.buying_price_list = source_doc.selling_price_list
|
||||
target_doc.is_internal_supplier = 1
|
||||
target_doc.inter_company_reference = source_doc.name
|
||||
else:
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.customer = details.get("party")
|
||||
target_doc.company_address = source_doc.supplier_address
|
||||
target_doc.shipping_address_name = source_doc.shipping_address
|
||||
target_doc.selling_price_list = source_doc.buying_price_list
|
||||
target_doc.is_internal_customer = 1
|
||||
target_doc.inter_company_reference = source_doc.name
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
doctype: {
|
||||
"doctype": target_doctype,
|
||||
"postprocess": update_details,
|
||||
"field_no_map": [
|
||||
"taxes_and_charges"
|
||||
]
|
||||
},
|
||||
doctype +" Item": {
|
||||
"doctype": target_doctype + " Item",
|
||||
"field_map": {
|
||||
source_document_warehouse_field: target_document_warehouse_field
|
||||
},
|
||||
"field_no_map": [
|
||||
"warehouse"
|
||||
]
|
||||
}
|
||||
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
@ -2,3 +2,22 @@
|
||||
|
||||
erpnext.setup_auto_gst_taxation('Delivery Note');
|
||||
|
||||
frappe.ui.form.on('Delivery Note', {
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus == 1 && !frm.is_dirty() && !frm.doc.ewaybill) {
|
||||
frm.add_custom_button('E-Way Bill JSON', () => {
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
||||
)
|
||||
);
|
||||
if (!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
}, __("Create"));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -22,7 +22,7 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
frappe.set_route("Form", lcv.doctype, lcv.name);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
frm.custom_make_buttons = {
|
||||
'Stock Entry': 'Return',
|
||||
'Purchase Invoice': 'Invoice'
|
||||
@ -40,7 +40,7 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
filters: {'company': frm.doc.company }
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
onload: function(frm) {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
@ -62,6 +62,15 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
}, __('Create'));
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.is_internal_supplier && !frm.doc.inter_company_reference) {
|
||||
frm.add_custom_button(__('Delivery Note'), function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: 'erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_inter_company_delivery_note',
|
||||
frm: cur_frm,
|
||||
})
|
||||
}, __('Create'));
|
||||
}
|
||||
},
|
||||
|
||||
company: function(frm) {
|
||||
|
@ -106,6 +106,8 @@
|
||||
"range",
|
||||
"column_break4",
|
||||
"per_billed",
|
||||
"is_internal_supplier",
|
||||
"inter_company_reference",
|
||||
"subscription_detail",
|
||||
"auto_repeat",
|
||||
"printing_settings",
|
||||
@ -1053,6 +1055,21 @@
|
||||
"oldfieldtype": "Date",
|
||||
"print_width": "100px",
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "supplier.is_internal_supplier",
|
||||
"fieldname": "is_internal_supplier",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Supplier",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "inter_company_reference",
|
||||
"fieldtype": "Link",
|
||||
"label": "Inter Company Reference",
|
||||
"options": "Delivery Note",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-truck",
|
||||
|
@ -17,6 +17,7 @@ from erpnext.buying.utils import check_on_hold_or_closed_status
|
||||
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
|
||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||
from six import iteritems
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -223,6 +224,7 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
if not stock_value_diff:
|
||||
continue
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[d.warehouse]["account"],
|
||||
"against": stock_rbnb,
|
||||
@ -231,17 +233,23 @@ class PurchaseReceipt(BuyingController):
|
||||
"debit": stock_value_diff
|
||||
}, warehouse_account[d.warehouse]["account_currency"], item=d))
|
||||
|
||||
# stock received but not billed
|
||||
stock_rbnb_currency = get_account_currency(stock_rbnb)
|
||||
# GL Entry for from warehouse or Stock Received but not billed
|
||||
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
|
||||
credit_currency = get_account_currency(warehouse_account[d.from_warehouse]['account']) \
|
||||
if d.from_warehouse else get_account_currency(stock_rbnb)
|
||||
|
||||
credit_amount = flt(d.base_net_amount, d.precision("base_net_amount")) \
|
||||
if credit_currency == self.company_currency else flt(d.net_amount, d.precision("net_amount"))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": stock_rbnb,
|
||||
"account": warehouse_account[d.from_warehouse]['account'] \
|
||||
if d.from_warehouse else stock_rbnb,
|
||||
"against": warehouse_account[d.warehouse]["account"],
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(d.base_net_amount, d.precision("base_net_amount")),
|
||||
"credit_in_account_currency": flt(d.base_net_amount, d.precision("base_net_amount")) \
|
||||
if stock_rbnb_currency==self.company_currency else flt(d.net_amount, d.precision("net_amount"))
|
||||
}, stock_rbnb_currency, item=d))
|
||||
"debit": -1 * flt(d.base_net_amount, d.precision("base_net_amount")),
|
||||
"debit_in_account_currency": -1 * credit_amount
|
||||
}, credit_currency, item=d))
|
||||
|
||||
negative_expense_to_be_booked += flt(d.item_tax_amount)
|
||||
|
||||
@ -287,7 +295,7 @@ class PurchaseReceipt(BuyingController):
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": divisional_loss,
|
||||
"project": d.project
|
||||
}, stock_rbnb_currency, item=d))
|
||||
}, credit_currency, item=d))
|
||||
|
||||
elif d.warehouse not in warehouse_with_no_account or \
|
||||
d.rejected_warehouse not in warehouse_with_no_account:
|
||||
@ -610,6 +618,10 @@ def make_stock_entry(source_name,target_doc=None):
|
||||
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_inter_company_delivery_note(source_name, target_doc=None):
|
||||
return make_inter_company_transaction("Purchase Receipt", source_name, target_doc)
|
||||
|
||||
def get_item_account_wise_additional_cost(purchase_document):
|
||||
landed_cost_vouchers = frappe.get_all("Landed Cost Purchase Receipt", fields=["parent"],
|
||||
filters = {"receipt_document": purchase_document, "docstatus": 1})
|
||||
|
@ -450,6 +450,83 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
self.assertEquals(pi2.items[0].qty, 2)
|
||||
self.assertEquals(pi2.items[1].qty, 1)
|
||||
|
||||
def test_stock_transfer_from_purchase_receipt(self):
|
||||
set_perpetual_inventory(1)
|
||||
pr = make_purchase_receipt(do_not_save=1)
|
||||
pr.supplier_warehouse = ''
|
||||
pr.items[0].from_warehouse = '_Test Warehouse 2 - _TC'
|
||||
|
||||
pr.submit()
|
||||
|
||||
gl_entries = get_gl_entries('Purchase Receipt', pr.name)
|
||||
sl_entries = get_sl_entries('Purchase Receipt', pr.name)
|
||||
|
||||
self.assertFalse(gl_entries)
|
||||
|
||||
expected_sle = {
|
||||
'_Test Warehouse 2 - _TC': -5,
|
||||
'_Test Warehouse - _TC': 5
|
||||
}
|
||||
|
||||
for sle in sl_entries:
|
||||
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def test_stock_transfer_from_purchase_receipt_with_valuation(self):
|
||||
set_perpetual_inventory(1)
|
||||
warehouse = frappe.get_doc('Warehouse', '_Test Warehouse 2 - _TC')
|
||||
warehouse.account = '_Test Account Stock In Hand - _TC'
|
||||
warehouse.save()
|
||||
|
||||
pr = make_purchase_receipt(do_not_save=1)
|
||||
pr.items[0].from_warehouse = '_Test Warehouse 2 - _TC'
|
||||
pr.supplier_warehouse = ''
|
||||
|
||||
|
||||
pr.append('taxes', {
|
||||
'charge_type': 'On Net Total',
|
||||
'account_head': '_Test Account Shipping Charges - _TC',
|
||||
'category': 'Valuation and Total',
|
||||
'cost_center': 'Main - _TC',
|
||||
'description': 'Test',
|
||||
'rate': 9
|
||||
})
|
||||
|
||||
pr.submit()
|
||||
|
||||
gl_entries = get_gl_entries('Purchase Receipt', pr.name)
|
||||
sl_entries = get_sl_entries('Purchase Receipt', pr.name)
|
||||
|
||||
expected_gle = [
|
||||
['Stock In Hand - _TC', 272.5, 0.0],
|
||||
['_Test Account Stock In Hand - _TC', 0.0, 250.0],
|
||||
['_Test Account Shipping Charges - _TC', 0.0, 22.5]
|
||||
]
|
||||
|
||||
expected_sle = {
|
||||
'_Test Warehouse 2 - _TC': -5,
|
||||
'_Test Warehouse - _TC': 5
|
||||
}
|
||||
|
||||
for sle in sl_entries:
|
||||
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEqual(gle.account, expected_gle[i][0])
|
||||
self.assertEqual(gle.debit, expected_gle[i][1])
|
||||
self.assertEqual(gle.credit, expected_gle[i][2])
|
||||
|
||||
warehouse.account = ''
|
||||
warehouse.save()
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
|
||||
def get_sl_entries(voucher_type, voucher_no):
|
||||
return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
|
||||
order by posting_time desc""", (voucher_type, voucher_no), as_dict=1)
|
||||
|
||||
def get_gl_entries(voucher_type, voucher_no):
|
||||
return frappe.db.sql("""select account, debit, credit, cost_center
|
||||
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-05-24 19:29:10",
|
||||
"doctype": "DocType",
|
||||
@ -67,6 +68,7 @@
|
||||
"warehouse_and_reference",
|
||||
"warehouse",
|
||||
"rejected_warehouse",
|
||||
"from_warehouse",
|
||||
"purchase_order",
|
||||
"material_request",
|
||||
"column_break_40",
|
||||
@ -815,15 +817,23 @@
|
||||
"fetch_from": "item_code.asset_category",
|
||||
"fieldname": "asset_category",
|
||||
"fieldtype": "Link",
|
||||
"in_preview": 1,
|
||||
"label": "Asset Category",
|
||||
"options": "Asset Category",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Supplier Warehouse",
|
||||
"options": "Warehouse"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2019-10-14 16:03:25.499557",
|
||||
"links": [],
|
||||
"modified": "2020-01-13 16:03:34.879827",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt Item",
|
||||
|
@ -973,6 +973,7 @@ def get_default_bom(item_code=None):
|
||||
if bom:
|
||||
return bom
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_valuation_rate(item_code, company, warehouse=None):
|
||||
item = get_item_defaults(item_code, company)
|
||||
item_group = get_item_group_defaults(item_code, company)
|
||||
|
Loading…
x
Reference in New Issue
Block a user