Merge branch 'develop' into putaway
This commit is contained in:
commit
406af27b9e
@ -14,7 +14,6 @@
|
||||
"column_break_9",
|
||||
"update_stock",
|
||||
"ignore_pricing_rule",
|
||||
"hide_unavailable_items",
|
||||
"warehouse",
|
||||
"campaign",
|
||||
"company_address",
|
||||
@ -23,6 +22,9 @@
|
||||
"section_break_11",
|
||||
"payments",
|
||||
"section_break_14",
|
||||
"hide_images",
|
||||
"hide_unavailable_items",
|
||||
"auto_add_item_to_cart",
|
||||
"item_groups",
|
||||
"column_break_16",
|
||||
"customer_groups",
|
||||
@ -124,7 +126,8 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Configuration"
|
||||
},
|
||||
{
|
||||
"description": "Only show Items from these Item Groups",
|
||||
@ -314,13 +317,25 @@
|
||||
"fieldname": "hide_unavailable_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Unavailable Items"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hide_images",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Images"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "auto_add_item_to_cart",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Add Filtered Item To Cart"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-29 13:18:38.795925",
|
||||
"modified": "2020-12-10 13:59:28.877572",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
@ -15,6 +15,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
return (doc.qty<=doc.received_qty) ? "green" : "orange";
|
||||
});
|
||||
}
|
||||
|
||||
this.frm.set_query("unrealized_profit_loss_account", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: doc.company,
|
||||
is_group: 0,
|
||||
root_type: "Liability",
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
onload: function() {
|
||||
this._super();
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -127,6 +126,7 @@
|
||||
"write_off_cost_center",
|
||||
"advances_section",
|
||||
"allocate_advances_automatically",
|
||||
"adjust_advance_taxes",
|
||||
"get_advances",
|
||||
"advances",
|
||||
"payment_schedule_section",
|
||||
@ -152,9 +152,11 @@
|
||||
"is_opening",
|
||||
"against_expense_account",
|
||||
"column_break_63",
|
||||
"unrealized_profit_loss_account",
|
||||
"status",
|
||||
"inter_company_invoice_reference",
|
||||
"is_internal_supplier",
|
||||
"represents_company",
|
||||
"remarks",
|
||||
"subscription_section",
|
||||
"from_date",
|
||||
@ -1223,7 +1225,7 @@
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled",
|
||||
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
@ -1330,13 +1332,37 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Taxes paid while advance payment will be adjusted against this invoice",
|
||||
"fieldname": "adjust_advance_taxes",
|
||||
"fieldtype": "Check",
|
||||
"label": "Adjust Advance Taxes"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_internal_supplier",
|
||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Unrealized Profit / Loss Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_internal_supplier",
|
||||
"description": "Company which internal supplier represents",
|
||||
"fetch_from": "supplier.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 13:57:18.266978",
|
||||
"modified": "2020-12-11 12:46:12.796378",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -206,8 +206,8 @@ class PurchaseInvoice(BuyingController):
|
||||
["Purchase Receipt", "purchase_receipt", "pr_detail"]
|
||||
])
|
||||
|
||||
def validate_warehouse(self):
|
||||
if self.update_stock:
|
||||
def validate_warehouse(self, for_validate=True):
|
||||
if self.update_stock and for_validate:
|
||||
for d in self.get('items'):
|
||||
if not d.warehouse:
|
||||
frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}").
|
||||
@ -233,7 +233,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
if self.update_stock:
|
||||
self.validate_item_code()
|
||||
self.validate_warehouse()
|
||||
self.validate_warehouse(for_validate)
|
||||
if auto_accounting_for_stock:
|
||||
warehouse_account = get_warehouse_account_map(self.company)
|
||||
|
||||
@ -449,6 +449,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_asset_gl_entry(gl_entries)
|
||||
|
||||
self.make_tax_gl_entries(gl_entries)
|
||||
self.make_internal_transfer_gl_entries(gl_entries)
|
||||
|
||||
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||
|
||||
@ -457,7 +458,6 @@ class PurchaseInvoice(BuyingController):
|
||||
self.make_payment_gl_entries(gl_entries)
|
||||
self.make_write_off_gl_entry(gl_entries)
|
||||
self.make_gle_for_rounding_adjustment(gl_entries)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def check_asset_cwip_enabled(self):
|
||||
@ -474,31 +474,30 @@ class PurchaseInvoice(BuyingController):
|
||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||
|
||||
if grand_total:
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"due_date": self.due_date,
|
||||
"against": self.against_expense_account,
|
||||
"credit": grand_total_in_company_currency,
|
||||
"credit_in_account_currency": grand_total_in_company_currency \
|
||||
if self.party_account_currency==self.company_currency else grand_total,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"project": self.project,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
if grand_total and not self.is_internal_transfer():
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"due_date": self.due_date,
|
||||
"against": self.against_expense_account,
|
||||
"credit": grand_total_in_company_currency,
|
||||
"credit_in_account_currency": grand_total_in_company_currency \
|
||||
if self.party_account_currency==self.company_currency else grand_total,
|
||||
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
"project": self.project,
|
||||
"cost_center": self.cost_center
|
||||
}, self.party_account_currency, item=self)
|
||||
)
|
||||
|
||||
def make_item_gl_entries(self, gl_entries):
|
||||
# item gl entries
|
||||
stock_items = self.get_stock_items()
|
||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||
if self.update_stock and self.auto_accounting_for_stock:
|
||||
warehouse_account = get_warehouse_account_map(self.company)
|
||||
|
||||
@ -526,7 +525,6 @@ class PurchaseInvoice(BuyingController):
|
||||
item, voucher_wise_stock_value, account_currency)
|
||||
|
||||
if item.from_warehouse:
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[item.warehouse]['account'],
|
||||
"against": warehouse_account[item.from_warehouse]["account"],
|
||||
@ -546,16 +544,18 @@ class PurchaseInvoice(BuyingController):
|
||||
"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)
|
||||
)
|
||||
# Do not book expense for transfer within same company transfer
|
||||
if not self.is_internal_transfer():
|
||||
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(
|
||||
@ -832,7 +832,8 @@ class PurchaseInvoice(BuyingController):
|
||||
}, account_currency, item=tax)
|
||||
)
|
||||
# accumulate valuation tax
|
||||
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
||||
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount) \
|
||||
and not self.is_internal_transfer():
|
||||
if self.auto_accounting_for_stock and not tax.cost_center:
|
||||
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
||||
valuation_tax.setdefault(tax.name, 0)
|
||||
@ -876,8 +877,19 @@ class PurchaseInvoice(BuyingController):
|
||||
"against": self.supplier,
|
||||
"credit": valuation_tax[tax.name],
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
}, item=tax)
|
||||
)
|
||||
}, item=tax))
|
||||
|
||||
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.unrealized_profit_loss_account,
|
||||
"against": self.supplier,
|
||||
"credit": flt(self.total_taxes_and_charges),
|
||||
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||
"cost_center": self.cost_center
|
||||
}, account_currency, item=self))
|
||||
|
||||
def make_payment_gl_entries(self, gl_entries):
|
||||
# Make Cash GL Entries
|
||||
@ -1095,7 +1107,9 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.docstatus == 2:
|
||||
status = "Cancelled"
|
||||
elif self.docstatus == 1:
|
||||
if outstanding_amount > 0 and due_date < nowdate:
|
||||
if self.is_internal_transfer():
|
||||
self.status = 'Internal Transfer'
|
||||
elif outstanding_amount > 0 and due_date < nowdate:
|
||||
self.status = "Overdue"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||
self.status = "Unpaid"
|
||||
|
@ -4,23 +4,25 @@
|
||||
// render
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return", "release_date", "on_hold"],
|
||||
"currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"],
|
||||
get_indicator: function(doc) {
|
||||
if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
||||
if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
||||
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||
if(cint(doc.on_hold) && !doc.release_date) {
|
||||
return [__("On Hold"), "darkgrey"];
|
||||
} else if(cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
||||
} else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
||||
return [__("Temporarily on Hold"), "darkgrey"];
|
||||
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||
} else if (frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||
}
|
||||
} else if(cint(doc.is_return)) {
|
||||
} else if (cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
} else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
|
||||
return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
|
||||
} else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
||||
}
|
||||
}
|
||||
|
@ -580,6 +580,16 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("unrealized_profit_loss_account", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
root_type: "Liability",
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.custom_make_buttons = {
|
||||
'Delivery Note': 'Delivery',
|
||||
'Sales Invoice': 'Sales Return',
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:05",
|
||||
@ -158,6 +157,7 @@
|
||||
"more_information",
|
||||
"inter_company_invoice_reference",
|
||||
"is_internal_customer",
|
||||
"represents_company",
|
||||
"customer_group",
|
||||
"campaign",
|
||||
"is_discounted",
|
||||
@ -171,6 +171,7 @@
|
||||
"c_form_applicable",
|
||||
"c_form_no",
|
||||
"column_break8",
|
||||
"unrealized_profit_loss_account",
|
||||
"remarks",
|
||||
"sales_team_section_break",
|
||||
"sales_partner",
|
||||
@ -1655,7 +1656,7 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled",
|
||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1950,13 +1951,31 @@
|
||||
"fieldtype": "Data",
|
||||
"label": "Company Tax ID",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_internal_customer",
|
||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Unrealized Profit / Loss Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_internal_customer",
|
||||
"description": "Company which internal customer represents",
|
||||
"fetch_from": "customer.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 13:57:45.086303",
|
||||
"modified": "2020-12-11 12:48:31.769958",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -758,6 +758,7 @@ class SalesInvoice(SellingController):
|
||||
self.make_customer_gl_entry(gl_entries)
|
||||
|
||||
self.make_tax_gl_entries(gl_entries)
|
||||
self.make_internal_transfer_gl_entries(gl_entries)
|
||||
|
||||
self.make_item_gl_entries(gl_entries)
|
||||
|
||||
@ -777,7 +778,7 @@ class SalesInvoice(SellingController):
|
||||
# Checked both rounding_adjustment and rounded_total
|
||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||
if grand_total:
|
||||
if grand_total and not self.is_internal_transfer():
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
@ -816,6 +817,18 @@ class SalesInvoice(SellingController):
|
||||
}, account_currency, item=tax)
|
||||
)
|
||||
|
||||
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.unrealized_profit_loss_account,
|
||||
"against": self.customer,
|
||||
"debit": flt(self.total_taxes_and_charges),
|
||||
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||
"cost_center": self.cost_center
|
||||
}, account_currency, item=self))
|
||||
|
||||
def make_item_gl_entries(self, gl_entries):
|
||||
# income account gl entries
|
||||
for item in self.get("items"):
|
||||
@ -838,22 +851,24 @@ class SalesInvoice(SellingController):
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||
else:
|
||||
income_account = (item.income_account
|
||||
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||
# Do not book income for transfer within same company
|
||||
if not self.is_internal_transfer():
|
||||
income_account = (item.income_account
|
||||
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||
|
||||
account_currency = get_account_currency(income_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": income_account,
|
||||
"against": self.customer,
|
||||
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||
if account_currency==self.company_currency
|
||||
else flt(item.net_amount, item.precision("net_amount"))),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
account_currency = get_account_currency(income_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": income_account,
|
||||
"against": self.customer,
|
||||
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||
if account_currency==self.company_currency
|
||||
else flt(item.net_amount, item.precision("net_amount"))),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
# expense account gl entries
|
||||
if cint(self.update_stock) and \
|
||||
@ -1265,7 +1280,9 @@ class SalesInvoice(SellingController):
|
||||
if self.docstatus == 2:
|
||||
status = "Cancelled"
|
||||
elif self.docstatus == 1:
|
||||
if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||
if self.is_internal_transfer():
|
||||
self.status = 'Internal Transfer'
|
||||
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||
self.status = "Overdue and Discounted"
|
||||
elif outstanding_amount > 0 and due_date < nowdate:
|
||||
self.status = "Overdue"
|
||||
@ -1530,9 +1547,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
|
||||
source_document_warehouse_field = 'target_warehouse'
|
||||
target_document_warehouse_field = 'from_warehouse'
|
||||
else:
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
|
||||
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)
|
||||
@ -1559,6 +1580,26 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
if currency:
|
||||
target_doc.currency = currency
|
||||
|
||||
item_field_map = {
|
||||
"doctype": target_doctype + " Item",
|
||||
"field_no_map": [
|
||||
"income_account",
|
||||
"expense_account",
|
||||
"cost_center",
|
||||
"warehouse"
|
||||
]
|
||||
}
|
||||
|
||||
if source_doc.get('update_stock'):
|
||||
item_field_map.update({
|
||||
'field_map': {
|
||||
source_document_warehouse_field: target_document_warehouse_field,
|
||||
'batch_no': 'batch_no',
|
||||
'serial_no': 'serial_no'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
doctype: {
|
||||
"doctype": target_doctype,
|
||||
@ -1567,15 +1608,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
"taxes_and_charges"
|
||||
]
|
||||
},
|
||||
doctype +" Item": {
|
||||
"doctype": target_doctype + " Item",
|
||||
"field_no_map": [
|
||||
"income_account",
|
||||
"expense_account",
|
||||
"cost_center",
|
||||
"warehouse"
|
||||
]
|
||||
}
|
||||
doctype +" Item": item_field_map
|
||||
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
|
@ -14,8 +14,8 @@ frappe.listview_settings['Sales Invoice'] = {
|
||||
"Credit Note Issued": "darkgrey",
|
||||
"Unpaid and Discounted": "orange",
|
||||
"Overdue and Discounted": "red",
|
||||
"Overdue": "red"
|
||||
|
||||
"Overdue": "red",
|
||||
"Internal Transfer": "darkgrey"
|
||||
};
|
||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||
},
|
||||
|
@ -1573,7 +1573,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
|
||||
def test_sales_invoice_with_project_link(self):
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
|
||||
@ -1607,9 +1607,9 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
|
||||
order by account asc""", sales_invoice.name, as_dict=1)
|
||||
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["project"], gle.project)
|
||||
|
||||
@ -1781,6 +1781,60 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||
|
||||
def test_internal_transfer_gl_entry(self):
|
||||
## Create internal transfer account
|
||||
account = create_account(account_name="Unrealized Profit",
|
||||
parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
|
||||
|
||||
frappe.db.set_value('Company', '_Test Company with perpetual inventory',
|
||||
'unrealized_profit_loss_account', account)
|
||||
|
||||
customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory")
|
||||
|
||||
create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory")
|
||||
|
||||
si = create_sales_invoice(
|
||||
company = "_Test Company with perpetual inventory",
|
||||
customer = customer,
|
||||
debit_to = "Debtors - TCP1",
|
||||
warehouse = "Stores - TCP1",
|
||||
income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1",
|
||||
cost_center = "Main - TCP1",
|
||||
currency = "INR",
|
||||
do_not_save = 1
|
||||
)
|
||||
|
||||
si.selling_price_list = "_Test Price List Rest of the World"
|
||||
si.update_stock = 1
|
||||
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||
add_taxes(si)
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
|
||||
target_doc.company = '_Test Company with perpetual inventory'
|
||||
target_doc.items[0].warehouse = 'Finished Goods - TCP1'
|
||||
add_taxes(target_doc)
|
||||
target_doc.save()
|
||||
target_doc.submit()
|
||||
|
||||
si_gl_entries = [
|
||||
["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
|
||||
["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
|
||||
|
||||
pi_gl_entries = [
|
||||
["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
|
||||
["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
|
||||
]
|
||||
|
||||
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||
|
||||
def test_eway_bill_json(self):
|
||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
@ -2039,4 +2093,57 @@ def get_taxes_and_charges():
|
||||
"parentfield": "taxes",
|
||||
"rate": 2,
|
||||
"row_id": 1
|
||||
}]
|
||||
}]
|
||||
|
||||
def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
|
||||
if not frappe.db.exists("Customer", customer_name):
|
||||
customer = frappe.get_doc({
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": customer_name,
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
"is_internal_customer": 1,
|
||||
"represents_company": represents_company
|
||||
})
|
||||
|
||||
customer.append("companies", {
|
||||
"company": allowed_to_interact_with
|
||||
})
|
||||
|
||||
customer.insert()
|
||||
customer_name = customer.name
|
||||
else:
|
||||
customer_name = frappe.db.get_value("Customer", customer_name)
|
||||
|
||||
return customer_name
|
||||
|
||||
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
||||
if not frappe.db.exists("Supplier", supplier_name):
|
||||
supplier = frappe.get_doc({
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": supplier_name,
|
||||
"doctype": "Supplier",
|
||||
"is_internal_supplier": 1,
|
||||
"represents_company": represents_company
|
||||
})
|
||||
|
||||
supplier.append("companies", {
|
||||
"company": allowed_to_interact_with
|
||||
})
|
||||
|
||||
supplier.insert()
|
||||
supplier_name = supplier.name
|
||||
else:
|
||||
supplier_name = frappe.db.exists("Supplier", supplier_name)
|
||||
|
||||
return supplier_name
|
||||
|
||||
def add_taxes(doc):
|
||||
doc.append('taxes', {
|
||||
'account_head': '_Test Account Excise Duty - TCP1',
|
||||
"charge_type": "On Net Total",
|
||||
"cost_center": "Main - TCP1",
|
||||
"description": "Excise Duty",
|
||||
"rate": 12
|
||||
})
|
@ -42,11 +42,13 @@
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
var start = filters.based_on_payment_terms ? 13 : 11;
|
||||
var range1 = report.columns[start].label;
|
||||
var range2 = report.columns[start+1].label;
|
||||
var range3 = report.columns[start+2].label;
|
||||
var range4 = report.columns[start+3].label;
|
||||
var range5 = report.columns[start+4].label;
|
||||
var range6 = report.columns[start+5].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
@ -70,20 +72,34 @@
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __(range6) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_number(balance_row["age"], null, 2) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range1"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range2"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range3"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range4"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(balance_row["range5"], data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("Future Payments") %}</td>
|
||||
<td></td>
|
||||
@ -91,6 +107,7 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
@ -101,6 +118,7 @@
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
@ -218,15 +236,15 @@
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %}</td>
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} </td>
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
@ -234,8 +252,8 @@
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
@ -256,10 +274,10 @@
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</tr>
|
||||
|
@ -8,6 +8,7 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
|
||||
get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
|
||||
get_group_by_conditions)
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
@ -22,7 +23,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
aii_account_map = get_aii_accounts()
|
||||
if item_list:
|
||||
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
|
||||
doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges")
|
||||
doctype='Purchase Invoice', tax_doctype='Purchase Taxes and Charges')
|
||||
|
||||
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
|
||||
|
||||
@ -34,10 +35,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if filters.get('group_by'):
|
||||
grand_total = get_grand_total(filters, 'Purchase Invoice')
|
||||
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
if not d.stock_qty:
|
||||
continue
|
||||
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
purchase_receipt = None
|
||||
if d.purchase_receipt:
|
||||
purchase_receipt = d.purchase_receipt
|
||||
@ -48,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': d.item_name,
|
||||
'item_group': d.item_group,
|
||||
'item_name': item_record.item_name,
|
||||
'item_group': item_record.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
@ -81,10 +86,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update({
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||
})
|
||||
total_tax += flt(item_tax.get("tax_amount"))
|
||||
total_tax += flt(item_tax.get('tax_amount'))
|
||||
|
||||
row.update({
|
||||
'total_tax': total_tax,
|
||||
@ -309,8 +314,8 @@ def get_items(filters, additional_query_columns):
|
||||
select
|
||||
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
||||
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||
|
@ -8,6 +8,7 @@ from frappe.utils import flt, cstr
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe.utils.xlsxutils import handle_html
|
||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
@ -16,7 +17,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if not filters: filters = {}
|
||||
columns = get_columns(additional_table_columns, filters)
|
||||
|
||||
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
|
||||
company_currency = frappe.get_cached_value('Company', filters.get('company'), 'default_currency')
|
||||
|
||||
item_list = get_items(filters, additional_query_columns)
|
||||
if item_list:
|
||||
@ -33,7 +34,13 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if filters.get('group_by'):
|
||||
grand_total = get_grand_total(filters, 'Sales Invoice')
|
||||
|
||||
customer_details = get_customer_details()
|
||||
item_details = get_item_details()
|
||||
|
||||
for d in item_list:
|
||||
customer_record = customer_details.get(d.customer)
|
||||
item_record = item_details.get(d.item_code)
|
||||
|
||||
delivery_note = None
|
||||
if d.delivery_note:
|
||||
delivery_note = d.delivery_note
|
||||
@ -45,14 +52,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
'item_name': d.item_name,
|
||||
'item_group': d.item_group,
|
||||
'item_name': item_record.item_name,
|
||||
'item_group': item_record.item_group,
|
||||
'description': d.description,
|
||||
'invoice': d.parent,
|
||||
'posting_date': d.posting_date,
|
||||
'customer': d.customer,
|
||||
'customer_name': d.customer_name,
|
||||
'customer_group': d.customer_group,
|
||||
'customer_name': customer_record.customer_name,
|
||||
'customer_group': customer_record.customer_group,
|
||||
}
|
||||
|
||||
if additional_query_columns:
|
||||
@ -90,10 +97,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||
row.update({
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
||||
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||
})
|
||||
total_tax += flt(item_tax.get("tax_amount"))
|
||||
total_tax += flt(item_tax.get('tax_amount'))
|
||||
|
||||
row.update({
|
||||
'total_tax': total_tax,
|
||||
@ -226,7 +233,7 @@ def get_columns(additional_table_columns, filters):
|
||||
if filters.get('group_by') != 'Territory':
|
||||
columns.extend([
|
||||
{
|
||||
'label': _("Territory"),
|
||||
'label': _('Territory'),
|
||||
'fieldname': 'territory',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Territory',
|
||||
@ -374,13 +381,12 @@ def get_items(filters, additional_query_columns):
|
||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name,
|
||||
`tabSales Invoice Item`.item_group, `tabSales Invoice Item`.description, `tabSales Invoice Item`.sales_order,
|
||||
`tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account,
|
||||
`tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty,
|
||||
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate,
|
||||
`tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name,
|
||||
`tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
|
||||
from `tabSales Invoice`, `tabSales Invoice Item`
|
||||
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||
@ -417,14 +423,14 @@ def get_deducted_taxes():
|
||||
return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
|
||||
|
||||
def get_tax_accounts(item_list, columns, company_currency,
|
||||
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
|
||||
doctype='Sales Invoice', tax_doctype='Sales Taxes and Charges'):
|
||||
import json
|
||||
item_row_map = {}
|
||||
tax_columns = []
|
||||
invoice_item_row = {}
|
||||
itemised_tax = {}
|
||||
|
||||
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"),
|
||||
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field('tax_amount'),
|
||||
currency=company_currency) or 2
|
||||
|
||||
for d in item_list:
|
||||
@ -469,8 +475,8 @@ def get_tax_accounts(item_list, columns, company_currency,
|
||||
tax_rate = tax_data
|
||||
tax_amount = 0
|
||||
|
||||
if charge_type == "Actual" and not tax_rate:
|
||||
tax_rate = "NA"
|
||||
if charge_type == 'Actual' and not tax_rate:
|
||||
tax_rate = 'NA'
|
||||
|
||||
item_net_amount = sum([flt(d.base_net_amount)
|
||||
for d in item_row_map.get(parent, {}).get(item_code, [])])
|
||||
@ -484,17 +490,17 @@ def get_tax_accounts(item_list, columns, company_currency,
|
||||
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
|
||||
|
||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||
"tax_rate": tax_rate,
|
||||
"tax_amount": tax_value
|
||||
'tax_rate': tax_rate,
|
||||
'tax_amount': tax_value
|
||||
})
|
||||
|
||||
except ValueError:
|
||||
continue
|
||||
elif charge_type == "Actual" and tax_amount:
|
||||
elif charge_type == 'Actual' and tax_amount:
|
||||
for d in invoice_item_row.get(parent, []):
|
||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||
"tax_rate": "NA",
|
||||
"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
||||
'tax_rate': 'NA',
|
||||
'tax_amount': flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
||||
tax_amount_precision)
|
||||
})
|
||||
|
||||
@ -563,7 +569,7 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map,
|
||||
})
|
||||
|
||||
total_row_map.setdefault('total_row', {
|
||||
subtotal_display_field: "Total",
|
||||
subtotal_display_field: 'Total',
|
||||
'stock_qty': 0.0,
|
||||
'amount': 0.0,
|
||||
'bold': 1,
|
||||
|
@ -25,7 +25,7 @@ class AssetValueAdjustment(Document):
|
||||
frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry))
|
||||
|
||||
self.reschedule_depreciations(self.current_asset_value)
|
||||
|
||||
|
||||
def validate_date(self):
|
||||
asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date')
|
||||
if getdate(self.date) < getdate(asset_purchase_date):
|
||||
@ -53,6 +53,7 @@ class AssetValueAdjustment(Document):
|
||||
je.posting_date = self.date
|
||||
je.company = self.company
|
||||
je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount)
|
||||
je.finance_book = self.finance_book
|
||||
|
||||
credit_entry = {
|
||||
"account": accumulated_depreciation_account,
|
||||
@ -78,7 +79,7 @@ class AssetValueAdjustment(Document):
|
||||
debit_entry.update({
|
||||
dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
|
||||
|
||||
je.append("accounts", credit_entry)
|
||||
je.append("accounts", debit_entry)
|
||||
|
||||
|
@ -75,24 +75,23 @@ def get_data(filters):
|
||||
for asset in assets_record:
|
||||
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
|
||||
- flt(depreciation_amount_map.get(asset.name))
|
||||
if asset_value:
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value
|
||||
}
|
||||
data.append(row)
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
@ -49,6 +49,12 @@ class Supplier(TransactionBase):
|
||||
msgprint(_("Series is mandatory"), raise_exception=1)
|
||||
|
||||
validate_party_accounts(self)
|
||||
self.validate_internal_supplier()
|
||||
|
||||
def validate_internal_supplier(self):
|
||||
if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
|
||||
frappe.throw(_("Internal Supplier for company {0} already exists").format(
|
||||
frappe.bold(self.represents_company)))
|
||||
|
||||
def on_trash(self):
|
||||
delete_contact_and_address('Supplier', self.name)
|
||||
|
@ -107,6 +107,8 @@ class AccountsController(TransactionBase):
|
||||
else:
|
||||
self.validate_deferred_start_and_end_date()
|
||||
|
||||
self.set_inter_company_account()
|
||||
|
||||
validate_regional(self)
|
||||
if self.doctype != 'Material Request':
|
||||
apply_pricing_rule_on_transaction(self)
|
||||
@ -932,6 +934,38 @@ class AccountsController(TransactionBase):
|
||||
else:
|
||||
return frappe.db.get_single_value("Global Defaults", "disable_rounded_total")
|
||||
|
||||
def set_inter_company_account(self):
|
||||
"""
|
||||
Set intercompany account for inter warehouse transactions
|
||||
This account will be used in case billing company and internal customer's
|
||||
representation company is same
|
||||
"""
|
||||
|
||||
if self.is_internal_transfer() and not self.unrealized_profit_loss_account:
|
||||
unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account')
|
||||
|
||||
if not unrealized_profit_loss_account:
|
||||
msg = _("Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}").format(
|
||||
frappe.bold(self.company))
|
||||
frappe.throw(msg)
|
||||
|
||||
self.unrealized_profit_loss_account = unrealized_profit_loss_account
|
||||
|
||||
def is_internal_transfer(self):
|
||||
"""
|
||||
It will an internal transfer if its an internal customer and representation
|
||||
company is same as billing company
|
||||
"""
|
||||
if self.doctype == 'Sales Invoice':
|
||||
internal_party_field = 'is_internal_customer'
|
||||
else:
|
||||
internal_party_field = 'is_internal_supplier'
|
||||
|
||||
if self.get(internal_party_field) and (self.represents_company == self.company):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
||||
|
@ -42,6 +42,7 @@ class BuyingController(StockController):
|
||||
self.validate_items()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.update_tax_category_for_internal_transfer()
|
||||
self.validate_warehouse()
|
||||
self.validate_from_warehouse()
|
||||
self.set_supplier_address()
|
||||
@ -94,13 +95,23 @@ class BuyingController(StockController):
|
||||
|
||||
def validate_stock_or_nonstock_items(self):
|
||||
if self.meta.get_field("taxes") and not self.get_stock_items() and not self.get_asset_items():
|
||||
tax_for_valuation = [d for d in self.get("taxes")
|
||||
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
|
||||
self.update_tax_category(msg)
|
||||
|
||||
def update_tax_category_for_internal_transfer(self):
|
||||
if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
|
||||
msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
|
||||
self.update_tax_category(msg)
|
||||
|
||||
def update_tax_category(self, msg):
|
||||
tax_for_valuation = [d for d in self.get("taxes")
|
||||
if d.category in ["Valuation", "Valuation and Total"]]
|
||||
|
||||
if tax_for_valuation:
|
||||
for d in tax_for_valuation:
|
||||
d.category = 'Total'
|
||||
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
|
||||
if tax_for_valuation:
|
||||
for d in tax_for_valuation:
|
||||
d.category = 'Total'
|
||||
|
||||
msgprint(msg)
|
||||
|
||||
def validate_asset_return(self):
|
||||
if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return:
|
||||
|
@ -254,22 +254,26 @@ class StatusUpdater(Document):
|
||||
if not args.get("second_source_extra_cond"):
|
||||
args["second_source_extra_cond"] = ""
|
||||
|
||||
args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s)
|
||||
args['second_source_condition'] = frappe.db.sql(""" select ifnull((select sum(%(second_source_field)s)
|
||||
from `tab%(second_source_dt)s`
|
||||
where `%(second_join_field)s`="%(detail_id)s"
|
||||
and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args
|
||||
and (`tab%(second_source_dt)s`.docstatus=1)
|
||||
%(second_source_extra_cond)s), 0) """ % args)[0][0]
|
||||
|
||||
if args['detail_id']:
|
||||
if not args.get("extra_cond"): args["extra_cond"] = ""
|
||||
|
||||
frappe.db.sql("""update `tab%(target_dt)s`
|
||||
set %(target_field)s = (
|
||||
args["source_dt_value"] = frappe.db.sql("""
|
||||
(select ifnull(sum(%(source_field)s), 0)
|
||||
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
|
||||
and (docstatus=1 %(cond)s) %(extra_cond)s)
|
||||
%(second_source_condition)s
|
||||
)
|
||||
%(update_modified)s
|
||||
""" % args)[0][0] or 0.0
|
||||
|
||||
if args['second_source_condition']:
|
||||
args["source_dt_value"] += flt(args['second_source_condition'])
|
||||
|
||||
frappe.db.sql("""update `tab%(target_dt)s`
|
||||
set %(target_field)s = %(source_dt_value)s %(update_modified)s
|
||||
where name='%(detail_id)s'""" % args)
|
||||
|
||||
def _update_percent_field_in_targets(self, args, update_modified=True):
|
||||
|
@ -79,7 +79,7 @@ class StockController(AccountsController):
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
if warehouse_account.get(sle.warehouse):
|
||||
# from warehouse account/ target warehouse account
|
||||
# from warehouse account
|
||||
|
||||
self.check_expense_account(item_row)
|
||||
|
||||
@ -94,9 +94,16 @@ class StockController(AccountsController):
|
||||
|
||||
sle = self.update_stock_ledger_entries(sle)
|
||||
|
||||
# expense account/ target_warehouse / source_warehouse
|
||||
if item_row.get('target_warehouse'):
|
||||
warehouse = item_row.get('target_warehouse')
|
||||
expense_account = warehouse_account[warehouse]["account"]
|
||||
else:
|
||||
expense_account = item_row.expense_account
|
||||
|
||||
gl_list.append(self.get_gl_dict({
|
||||
"account": warehouse_account[sle.warehouse]["account"],
|
||||
"against": item_row.expense_account,
|
||||
"against": expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.project or self.get('project'),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
@ -104,9 +111,8 @@ 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))
|
||||
|
||||
# expense account
|
||||
gl_list.append(self.get_gl_dict({
|
||||
"account": item_row.expense_account,
|
||||
"account": expense_account,
|
||||
"against": warehouse_account[sle.warehouse]["account"],
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.project or self.get('project'),
|
||||
|
@ -519,6 +519,17 @@ class calculate_taxes_and_totals(object):
|
||||
if self.doc.docstatus == 0:
|
||||
self.calculate_outstanding_amount()
|
||||
|
||||
def is_internal_invoice(self):
|
||||
"""
|
||||
Checks if its an internal transfer invoice
|
||||
and decides if to calculate any out standing amount or not
|
||||
"""
|
||||
|
||||
if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def calculate_outstanding_amount(self):
|
||||
# NOTE:
|
||||
# write_off_amount is only for POS Invoice
|
||||
@ -526,7 +537,8 @@ class calculate_taxes_and_totals(object):
|
||||
if self.doc.doctype == "Sales Invoice":
|
||||
self.calculate_paid_amount()
|
||||
|
||||
if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos'): return
|
||||
if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
|
||||
self.is_internal_invoice(): return
|
||||
|
||||
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
|
||||
self._set_in_company_currency(self.doc, ['write_off_amount'])
|
||||
|
@ -1,23 +1,31 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
cur_frm.add_fetch("contract_template", "contract_terms", "contract_terms");
|
||||
cur_frm.add_fetch("contract_template", "requires_fulfilment", "requires_fulfilment");
|
||||
|
||||
// Add fulfilment terms from contract template into contract
|
||||
frappe.ui.form.on("Contract", {
|
||||
contract_template: function (frm) {
|
||||
// Populate the fulfilment terms table from a contract template, if any
|
||||
if (frm.doc.contract_template) {
|
||||
frappe.model.with_doc("Contract Template", frm.doc.contract_template, function () {
|
||||
var tabletransfer = frappe.model.get_doc("Contract Template", frm.doc.contract_template);
|
||||
|
||||
frm.doc.fulfilment_terms = [];
|
||||
$.each(tabletransfer.fulfilment_terms, function (index, row) {
|
||||
var d = frm.add_child("fulfilment_terms");
|
||||
d.requirement = row.requirement;
|
||||
frm.refresh_field("fulfilment_terms");
|
||||
});
|
||||
frappe.call({
|
||||
method: 'erpnext.crm.doctype.contract_template.contract_template.get_contract_template',
|
||||
args: {
|
||||
template_name: frm.doc.contract_template,
|
||||
doc: frm.doc
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r && r.message) {
|
||||
let contract_template = r.message.contract_template;
|
||||
frm.set_value("contract_terms", r.message.contract_terms);
|
||||
frm.set_value("requires_fulfilment", contract_template.requires_fulfilment);
|
||||
|
||||
if (frm.doc.requires_fulfilment) {
|
||||
// Populate the fulfilment terms table from a contract template, if any
|
||||
r.message.contract_template.fulfilment_terms.forEach(element => {
|
||||
let d = frm.add_child("fulfilment_terms");
|
||||
d.requirement = element.requirement;
|
||||
});
|
||||
frm.refresh_field("fulfilment_terms");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"creation": "2018-04-12 06:32:04.582486",
|
||||
@ -247,7 +248,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-03-30 06:56:07.257932",
|
||||
"modified": "2020-12-07 11:15:58.385521",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Contract",
|
||||
|
@ -11,7 +11,9 @@
|
||||
"contract_terms",
|
||||
"sb_fulfilment",
|
||||
"requires_fulfilment",
|
||||
"fulfilment_terms"
|
||||
"fulfilment_terms",
|
||||
"section_break_6",
|
||||
"contract_template_help"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -41,10 +43,20 @@
|
||||
"fieldtype": "Table",
|
||||
"label": "Fulfilment Terms and Conditions",
|
||||
"options": "Contract Template Fulfilment Terms"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "contract_template_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Contract Template Help",
|
||||
"options": "<h4>Contract Template Example</h4>\n\n<pre>Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Language. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-11-11 17:49:44.879363",
|
||||
"modified": "2020-12-07 10:44:22.587047",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Contract Template",
|
||||
|
@ -5,6 +5,27 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.jinja import validate_template
|
||||
from six import string_types
|
||||
import json
|
||||
|
||||
class ContractTemplate(Document):
|
||||
pass
|
||||
def validate(self):
|
||||
if self.contract_terms:
|
||||
validate_template(self.contract_terms)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_contract_template(template_name, doc):
|
||||
if isinstance(doc, string_types):
|
||||
doc = json.loads(doc)
|
||||
|
||||
contract_template = frappe.get_doc("Contract Template", template_name)
|
||||
contract_terms = None
|
||||
|
||||
if contract_template.contract_terms:
|
||||
contract_terms = frappe.render_template(contract_template.contract_terms, doc)
|
||||
|
||||
return {
|
||||
'contract_template': contract_template,
|
||||
'contract_terms': contract_terms
|
||||
}
|
@ -260,6 +260,15 @@ def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
|
||||
"""Shipping lines represents the shipping details,
|
||||
each such shipping detail consists of a list of tax_lines"""
|
||||
for shipping_charge in shipping_lines:
|
||||
if shipping_charge.get("price"):
|
||||
taxes.append({
|
||||
"charge_type": _("Actual"),
|
||||
"account_head": get_tax_account_head(shipping_charge),
|
||||
"description": shipping_charge["title"],
|
||||
"tax_amount": shipping_charge["price"],
|
||||
"cost_center": shopify_settings.cost_center
|
||||
})
|
||||
|
||||
for tax in shipping_charge.get("tax_lines"):
|
||||
taxes.append({
|
||||
"charge_type": _("Actual"),
|
||||
|
@ -30,6 +30,11 @@
|
||||
"label": "Laboratory",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Inpatient",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Rehabilitation and Physiotherapy",
|
||||
@ -38,7 +43,7 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Records and History",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]"
|
||||
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
@ -64,7 +69,7 @@
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Healthcare",
|
||||
"modified": "2020-11-23 23:00:48.764377",
|
||||
"modified": "2020-11-26 22:09:09.164584",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Healthcare",
|
||||
|
@ -29,6 +29,29 @@ frappe.ui.form.on('Inpatient Medication Entry', {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (frm.doc.__islocal || frm.doc.docstatus !== 0 || !frm.doc.update_stock)
|
||||
return;
|
||||
|
||||
frm.add_custom_button(__('Make Stock Entry'), function() {
|
||||
frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry.make_difference_stock_entry',
|
||||
args: { docname: frm.doc.name },
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
|
||||
} else {
|
||||
frappe.msgprint({
|
||||
title: __('No Drug Shortage'),
|
||||
message: __('All the drugs are available with sufficient qty to process this Inpatient Medication Entry.'),
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
patient: function(frm) {
|
||||
|
@ -142,25 +142,32 @@ class InpatientMedicationEntry(Document):
|
||||
return orders, order_entry_map
|
||||
|
||||
def check_stock_qty(self):
|
||||
from erpnext.stock.stock_ledger import NegativeStockError
|
||||
drug_shortage = get_drug_shortage_map(self.medication_orders, self.warehouse)
|
||||
|
||||
drug_availability = dict()
|
||||
for d in self.medication_orders:
|
||||
if not drug_availability.get(d.drug_code):
|
||||
drug_availability[d.drug_code] = 0
|
||||
drug_availability[d.drug_code] += flt(d.dosage)
|
||||
if drug_shortage:
|
||||
message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse))
|
||||
message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.')
|
||||
|
||||
for drug, dosage in drug_availability.items():
|
||||
available_qty = get_latest_stock_qty(drug, self.warehouse)
|
||||
formatted_item_rows = ''
|
||||
|
||||
# validate qty
|
||||
if flt(available_qty) < flt(dosage):
|
||||
frappe.throw(_('Quantity not available for {0} in warehouse {1}').format(
|
||||
frappe.bold(drug), frappe.bold(self.warehouse))
|
||||
+ '<br><br>' + _('Available quantity is {0}, you need {1}').format(
|
||||
frappe.bold(available_qty), frappe.bold(dosage))
|
||||
+ '<br><br>' + _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.'),
|
||||
NegativeStockError, title=_('Insufficient Stock'))
|
||||
for drug, shortage_qty in drug_shortage.items():
|
||||
item_link = get_link_to_form('Item', drug)
|
||||
formatted_item_rows += """
|
||||
<td>{0}</td>
|
||||
<td>{1}</td>
|
||||
</tr>""".format(item_link, frappe.bold(shortage_qty))
|
||||
|
||||
message += """
|
||||
<table class='table'>
|
||||
<thead>
|
||||
<th>{0}</th>
|
||||
<th>{1}</th>
|
||||
</thead>
|
||||
{2}
|
||||
</table>
|
||||
""".format(_('Drug Code'), _('Shortage Qty'), formatted_item_rows)
|
||||
|
||||
frappe.throw(message, title=_('Insufficient Stock'), is_minimizable=True, wide=True)
|
||||
|
||||
def make_stock_entry(self):
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
@ -223,7 +230,8 @@ def get_pending_medication_orders(entry):
|
||||
|
||||
for doc in data:
|
||||
inpatient_record = doc.inpatient_record
|
||||
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
||||
if inpatient_record:
|
||||
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
||||
|
||||
if entry.service_unit and doc.service_unit != entry.service_unit:
|
||||
to_remove.append(doc)
|
||||
@ -276,4 +284,55 @@ def get_current_healthcare_service_unit(inpatient_record):
|
||||
ip_record = frappe.get_doc('Inpatient Record', inpatient_record)
|
||||
if ip_record.inpatient_occupancies:
|
||||
return ip_record.inpatient_occupancies[-1].service_unit
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
def get_drug_shortage_map(medication_orders, warehouse):
|
||||
"""
|
||||
Returns a dict like { drug_code: shortage_qty }
|
||||
"""
|
||||
drug_requirement = dict()
|
||||
for d in medication_orders:
|
||||
if not drug_requirement.get(d.drug_code):
|
||||
drug_requirement[d.drug_code] = 0
|
||||
drug_requirement[d.drug_code] += flt(d.dosage)
|
||||
|
||||
drug_shortage = dict()
|
||||
for drug, required_qty in drug_requirement.items():
|
||||
available_qty = get_latest_stock_qty(drug, warehouse)
|
||||
if flt(required_qty) > flt(available_qty):
|
||||
drug_shortage[drug] = flt(flt(required_qty) - flt(available_qty))
|
||||
|
||||
return drug_shortage
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_difference_stock_entry(docname):
|
||||
doc = frappe.get_doc('Inpatient Medication Entry', docname)
|
||||
drug_shortage = get_drug_shortage_map(doc.medication_orders, doc.warehouse)
|
||||
|
||||
if not drug_shortage:
|
||||
return None
|
||||
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
stock_entry.purpose = 'Material Transfer'
|
||||
stock_entry.set_stock_entry_type()
|
||||
stock_entry.to_warehouse = doc.warehouse
|
||||
stock_entry.company = doc.company
|
||||
cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
|
||||
|
||||
for drug, shortage_qty in drug_shortage.items():
|
||||
se_child = stock_entry.append('items')
|
||||
se_child.item_code = drug
|
||||
se_child.item_name = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||
se_child.uom = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||
se_child.stock_uom = se_child.uom
|
||||
se_child.qty = flt(shortage_qty)
|
||||
se_child.t_warehouse = doc.warehouse
|
||||
# in stock uom
|
||||
se_child.conversion_factor = 1
|
||||
se_child.cost_center = cost_center
|
||||
se_child.expense_account = expense_account
|
||||
|
||||
return stock_entry
|
||||
|
@ -9,6 +9,7 @@ from frappe.utils import add_days, getdate, now_datetime
|
||||
from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
||||
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
|
||||
from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
|
||||
from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_drug_shortage_map, make_difference_stock_entry
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
||||
|
||||
class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
@ -82,6 +83,39 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
self.assertEqual(stock_entry.items[0].patient, self.patient)
|
||||
self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
|
||||
|
||||
def test_drug_shortage_stock_entry(self):
|
||||
ipmo = create_ipmo(self.patient)
|
||||
ipmo.submit()
|
||||
ipmo.reload()
|
||||
|
||||
date = add_days(getdate(), -1)
|
||||
filters = frappe._dict(
|
||||
from_date=date,
|
||||
to_date=date,
|
||||
from_time='',
|
||||
to_time='',
|
||||
item_code='Dextromethorphan',
|
||||
patient=self.patient
|
||||
)
|
||||
|
||||
# check drug shortage
|
||||
ipme = create_ipme(filters, update_stock=1)
|
||||
ipme.warehouse = 'Finished Goods - _TC'
|
||||
ipme.save()
|
||||
drug_shortage = get_drug_shortage_map(ipme.medication_orders, ipme.warehouse)
|
||||
self.assertEqual(drug_shortage.get('Dextromethorphan'), 3)
|
||||
|
||||
# check material transfer for drug shortage
|
||||
make_stock_entry()
|
||||
stock_entry = make_difference_stock_entry(ipme.name)
|
||||
self.assertEqual(stock_entry.items[0].item_code, 'Dextromethorphan')
|
||||
self.assertEqual(stock_entry.items[0].qty, 3)
|
||||
stock_entry.from_warehouse = 'Stores - _TC'
|
||||
stock_entry.submit()
|
||||
|
||||
ipme.reload()
|
||||
ipme.submit()
|
||||
|
||||
def tearDown(self):
|
||||
# cleanup - Discharge
|
||||
schedule_discharge(frappe.as_json({'patient': self.patient}))
|
||||
@ -94,15 +128,12 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
for entry in frappe.get_all('Inpatient Medication Entry'):
|
||||
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
||||
doc.cancel()
|
||||
frappe.db.delete('Stock Entry', {'inpatient_medication_entry': doc.name})
|
||||
doc.delete()
|
||||
|
||||
for entry in frappe.get_all('Inpatient Medication Order'):
|
||||
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
def make_stock_entry():
|
||||
def make_stock_entry(warehouse=None):
|
||||
frappe.db.set_value('Company', '_Test Company', {
|
||||
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
||||
'default_inventory_account': 'Stock In Hand - _TC'
|
||||
@ -110,7 +141,7 @@ def make_stock_entry():
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
stock_entry.stock_entry_type = 'Material Receipt'
|
||||
stock_entry.company = '_Test Company'
|
||||
stock_entry.to_warehouse = 'Stores - _TC'
|
||||
stock_entry.to_warehouse = warehouse or 'Stores - _TC'
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
|
||||
se_child = stock_entry.append('items')
|
||||
se_child.item_code = 'Dextromethorphan'
|
||||
|
@ -18,6 +18,10 @@ def get_data():
|
||||
{
|
||||
'label': _('Billing'),
|
||||
'items': ['Sales Invoice']
|
||||
},
|
||||
{
|
||||
'label': _('Orders'),
|
||||
'items': ['Inpatient Medication Order']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -491,6 +491,39 @@ class TestWorkOrder(unittest.TestCase):
|
||||
work_order1.save()
|
||||
self.assertEqual(work_order1.operations[0].time_in_mins, 40.0)
|
||||
|
||||
def test_partial_material_consumption(self):
|
||||
frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1)
|
||||
wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
|
||||
|
||||
ste_cancel_list = []
|
||||
ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||
target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0)
|
||||
ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||
target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0)
|
||||
|
||||
ste_cancel_list.extend([ste1, ste2])
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4))
|
||||
s.submit()
|
||||
ste_cancel_list.append(s)
|
||||
|
||||
ste1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
|
||||
ste1.submit()
|
||||
ste_cancel_list.append(ste1)
|
||||
|
||||
print(wo_order.name)
|
||||
ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2))
|
||||
self.assertEquals(ste3.fg_completed_qty, 2)
|
||||
|
||||
expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4}
|
||||
for row in ste3.items:
|
||||
self.assertEquals(row.qty, expected_qty.get(row.item_code))
|
||||
|
||||
for ste_doc in ste_cancel_list:
|
||||
ste_doc.cancel()
|
||||
|
||||
frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0)
|
||||
|
||||
def get_scrap_item_details(bom_no):
|
||||
scrap_items = {}
|
||||
for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
|
||||
|
@ -545,7 +545,8 @@ erpnext.work_order = {
|
||||
var tbl = frm.doc.required_items || [];
|
||||
var tbl_lenght = tbl.length;
|
||||
for (var i = 0, len = tbl_lenght; i < len; i++) {
|
||||
if (flt(frm.doc.required_items[i].required_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
||||
let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
|
||||
if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ class ProductionPlanReport(object):
|
||||
if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)")
|
||||
|
||||
raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code,
|
||||
bom_item.item_name as raw_material_name, {0} as required_qty
|
||||
bom_item.item_name as raw_material_name, {0} as required_qty_per_unit
|
||||
FROM
|
||||
`tabBOM` as bom, `tab{1}` as bom_item
|
||||
WHERE
|
||||
@ -208,7 +208,7 @@ class ProductionPlanReport(object):
|
||||
warehouses = self.mrp_warehouses or []
|
||||
for d in self.raw_materials_dict.get(key):
|
||||
if self.filters.based_on != "Work Order":
|
||||
d.required_qty = d.required_qty * data.qty_to_manufacture
|
||||
d.required_qty = d.required_qty_per_unit * data.qty_to_manufacture
|
||||
|
||||
if not warehouses:
|
||||
warehouses = [data.warehouse]
|
||||
|
@ -5,6 +5,8 @@ from frappe.utils import nowdate
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
||||
from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
|
||||
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import get_accrued_interest_entries
|
||||
from frappe.model.naming import make_autoname
|
||||
|
||||
def execute():
|
||||
|
||||
@ -18,15 +20,29 @@ def execute():
|
||||
frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail')
|
||||
frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual')
|
||||
frappe.reload_doc('accounts', 'doctype', 'gl_entry')
|
||||
frappe.reload_doc('accounts', 'doctype', 'journal_entry_account')
|
||||
|
||||
updated_loan_types = []
|
||||
loans_to_close = []
|
||||
|
||||
# Update old loan status as closed
|
||||
if frappe.db.has_column('Repayment Schedule', 'paid'):
|
||||
loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule`
|
||||
where paid = 0 and docstatus = 1""", as_dict=1)
|
||||
|
||||
loans_to_close = [d.parent for d in loans_list]
|
||||
|
||||
if loans_to_close:
|
||||
frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close))
|
||||
|
||||
loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment',
|
||||
'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'])
|
||||
'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'],
|
||||
filters={'docstatus': 1, 'status': ('!=', 'Closed')})
|
||||
|
||||
for loan in loans:
|
||||
# Update details in Loan Types and Loan
|
||||
loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company')
|
||||
loan_type = loan.loan_type
|
||||
|
||||
group_income_account = frappe.get_value('Account', {'company': loan.company,
|
||||
'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')})
|
||||
@ -38,7 +54,26 @@ def execute():
|
||||
penalty_account = create_account(company=loan.company, account_type='Income Account',
|
||||
account_name='Penalty Account', parent_account=group_income_account)
|
||||
|
||||
if not loan_type_company:
|
||||
# Same loan type used for multiple companies
|
||||
if loan_type_company and loan_type_company != loan.company:
|
||||
# get loan type for appropriate company
|
||||
loan_type_name = frappe.get_value('Loan Type', {'company': loan.company,
|
||||
'mode_of_payment': loan.mode_of_payment, 'loan_account': loan.loan_account,
|
||||
'payment_account': loan.payment_account, 'interest_income_account': loan.interest_income_account,
|
||||
'penalty_income_account': loan.penalty_income_account}, 'name')
|
||||
|
||||
if not loan_type_name:
|
||||
loan_type_name = create_loan_type(loan, loan_type_name, penalty_account)
|
||||
|
||||
# update loan type in loan
|
||||
frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name,
|
||||
loan.name))
|
||||
|
||||
loan_type = loan_type_name
|
||||
if loan_type_name not in updated_loan_types:
|
||||
updated_loan_types.append(loan_type_name)
|
||||
|
||||
elif not loan_type_company:
|
||||
loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type)
|
||||
loan_type_doc.is_term_loan = 1
|
||||
loan_type_doc.company = loan.company
|
||||
@ -49,8 +84,9 @@ def execute():
|
||||
loan_type_doc.penalty_income_account = penalty_account
|
||||
loan_type_doc.submit()
|
||||
updated_loan_types.append(loan.loan_type)
|
||||
loan_type = loan.loan_type
|
||||
|
||||
if loan.loan_type in updated_loan_types:
|
||||
if loan_type in updated_loan_types:
|
||||
if loan.status == 'Fully Disbursed':
|
||||
status = 'Disbursed'
|
||||
elif loan.status == 'Repaid/Closed':
|
||||
@ -64,25 +100,48 @@ def execute():
|
||||
'status': status
|
||||
})
|
||||
|
||||
process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan.loan_type,
|
||||
process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type,
|
||||
loan=loan.name)
|
||||
|
||||
payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date
|
||||
FROM `tabJournal Entry` j, `tabJournal Entry Account` a
|
||||
WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s
|
||||
and account = %s
|
||||
''', (loan.name, loan.loan_account), as_dict=1)
|
||||
|
||||
for payment in payments:
|
||||
repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant,
|
||||
loan.loan_type, loan.company)
|
||||
if frappe.db.has_column('Repayment Schedule', 'paid'):
|
||||
total_principal, total_interest = frappe.db.get_value('Repayment Schedule', {'paid': 1, 'parent': loan.name},
|
||||
['sum(principal_amount) as total_principal', 'sum(interest_amount) as total_interest'])
|
||||
|
||||
repayment_entry.amount_paid = payment.debit_in_account_currency
|
||||
repayment_entry.posting_date = payment.posting_date
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
accrued_entries = get_accrued_interest_entries(loan.name)
|
||||
for entry in accrued_entries:
|
||||
interest_paid = 0
|
||||
principal_paid = 0
|
||||
|
||||
jv = frappe.get_doc('Journal Entry', payment.name)
|
||||
jv.flags.ignore_links = True
|
||||
jv.cancel()
|
||||
if total_interest > entry.interest_amount:
|
||||
interest_paid = entry.interest_amount
|
||||
else:
|
||||
interest_paid = total_interest
|
||||
|
||||
if total_principal > entry.payable_principal_amount:
|
||||
principal_paid = entry.payable_principal_amount
|
||||
else:
|
||||
principal_paid = total_principal
|
||||
|
||||
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
|
||||
SET paid_principal_amount = `paid_principal_amount` + %s,
|
||||
paid_interest_amount = `paid_interest_amount` + %s
|
||||
WHERE name = %s""",
|
||||
(principal_paid, interest_paid, entry.name))
|
||||
|
||||
total_principal -= principal_paid
|
||||
total_interest -= interest_paid
|
||||
|
||||
def create_loan_type(loan, loan_type_name, penalty_account):
|
||||
loan_type_doc = frappe.new_doc('Loan Type')
|
||||
loan_type_doc.loan_name = make_autoname("Loan Type-.####")
|
||||
loan_type_doc.is_term_loan = 1
|
||||
loan_type_doc.company = loan.company
|
||||
loan_type_doc.mode_of_payment = loan.mode_of_payment
|
||||
loan_type_doc.payment_account = loan.payment_account
|
||||
loan_type_doc.loan_account = loan.loan_account
|
||||
loan_type_doc.interest_income_account = loan.interest_income_account
|
||||
loan_type_doc.penalty_income_account = penalty_account
|
||||
loan_type_doc.submit()
|
||||
|
||||
return loan_type_doc.name
|
||||
|
@ -609,6 +609,15 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
this.calculate_outstanding_amount(update_paid_amount);
|
||||
},
|
||||
|
||||
is_internal_invoice: function() {
|
||||
if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) {
|
||||
if (this.frm.doc.company === this.frm.doc.represents_company) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
calculate_outstanding_amount: function(update_paid_amount) {
|
||||
// NOTE:
|
||||
// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
|
||||
@ -617,7 +626,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
this.calculate_paid_amount();
|
||||
}
|
||||
|
||||
if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return;
|
||||
if (this.frm.doc.is_return || (this.frm.doc.docstatus > 0) || this.is_internal_invoice()) return;
|
||||
|
||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
|
||||
|
||||
|
@ -408,7 +408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
|
||||
show_description(row_to_modify.idx, row_to_modify.item_code);
|
||||
|
||||
this.frm.from_barcode = true;
|
||||
this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
|
||||
frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
|
||||
item_code: data.item_code,
|
||||
qty: (row_to_modify.qty || 0) + 1
|
||||
@ -492,7 +492,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
d.item_code = "";
|
||||
}
|
||||
|
||||
this.frm.from_barcode = true;
|
||||
this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
|
||||
this.item_code(doc, cdt, cdn);
|
||||
},
|
||||
|
||||
@ -509,11 +509,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
show_batch_dialog = 1;
|
||||
}
|
||||
// clear barcode if setting item (else barcode will take priority)
|
||||
if(!this.frm.from_barcode) {
|
||||
if (this.frm.from_barcode == 0) {
|
||||
item.barcode = null;
|
||||
}
|
||||
this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0;
|
||||
|
||||
|
||||
this.frm.from_barcode = false;
|
||||
if(item.item_code || item.barcode || item.serial_no) {
|
||||
if(!this.validate_company_and_party()) {
|
||||
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
|
||||
|
@ -151,6 +151,7 @@ class Gstr1Report(object):
|
||||
{select_columns}
|
||||
from `tab{doctype}`
|
||||
where docstatus = 1 {where_conditions}
|
||||
and is_opening = 'No'
|
||||
order by posting_date desc
|
||||
""".format(select_columns=self.select_columns, doctype=self.doctype,
|
||||
where_conditions=conditions), self.filters, as_dict=1)
|
||||
|
@ -58,6 +58,7 @@ class Customer(TransactionBase):
|
||||
self.set_loyalty_program()
|
||||
self.check_customer_group_change()
|
||||
self.validate_default_bank_account()
|
||||
self.validate_internal_customer()
|
||||
|
||||
# set loyalty program tier
|
||||
if frappe.db.exists('Customer', self.name):
|
||||
@ -82,6 +83,11 @@ class Customer(TransactionBase):
|
||||
if not is_company_account:
|
||||
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
|
||||
|
||||
def validate_internal_customer(self):
|
||||
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
|
||||
frappe.throw(_("Internal Customer for company {0} already exists").format(
|
||||
frappe.bold(self.represents_company)))
|
||||
|
||||
def on_update(self):
|
||||
self.validate_name_with_customer_group()
|
||||
self.create_primary_contact()
|
||||
@ -398,7 +404,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
||||
# form a list of emails and names to show to the user
|
||||
credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
|
||||
if not credit_controller_users_formatted:
|
||||
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
|
||||
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.").format(customer))
|
||||
|
||||
message = """Please contact any of the following users to extend the credit limits for {0}:
|
||||
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
|
||||
|
@ -111,24 +111,24 @@ erpnext.PointOfSale.Controller = class {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
prepare_app_defaults(data) {
|
||||
async prepare_app_defaults(data) {
|
||||
this.pos_opening = data.name;
|
||||
this.company = data.company;
|
||||
this.pos_profile = data.pos_profile;
|
||||
this.pos_opening_time = data.period_start_date;
|
||||
this.item_stock_map = {};
|
||||
this.settings = {};
|
||||
|
||||
frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => {
|
||||
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
|
||||
});
|
||||
|
||||
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
||||
this.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||
this.cart.make_customer_selector();
|
||||
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||
this.settings.hide_images = profile.hide_images;
|
||||
this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
|
||||
this.make_app();
|
||||
});
|
||||
|
||||
this.item_stock_map = {};
|
||||
|
||||
this.make_app();
|
||||
}
|
||||
|
||||
set_opening_entry_status() {
|
||||
@ -238,12 +238,11 @@ erpnext.PointOfSale.Controller = class {
|
||||
this.item_selector = new erpnext.PointOfSale.ItemSelector({
|
||||
wrapper: this.$components_wrapper,
|
||||
pos_profile: this.pos_profile,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
item_selected: args => this.on_cart_update(args),
|
||||
|
||||
get_frm: () => this.frm || {},
|
||||
|
||||
get_allowed_item_group: () => this.item_groups
|
||||
get_frm: () => this.frm || {}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -251,6 +250,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
init_item_cart() {
|
||||
this.cart = new erpnext.PointOfSale.ItemCart({
|
||||
wrapper: this.$components_wrapper,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
@ -273,9 +273,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
this.customer_details = details;
|
||||
// will add/remove LP payment method
|
||||
this.payment.render_loyalty_points_payment_mode();
|
||||
},
|
||||
|
||||
get_allowed_customer_group: () => this.customer_groups
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
erpnext.PointOfSale.ItemCart = class {
|
||||
constructor({ wrapper, events }) {
|
||||
constructor({ wrapper, events, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.customer_info = undefined;
|
||||
this.hide_images = settings.hide_images;
|
||||
this.allowed_customer_groups = settings.customer_groups;
|
||||
|
||||
this.init_component();
|
||||
}
|
||||
@ -32,6 +34,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
|
||||
)
|
||||
this.$customer_section = this.$component.find('.customer-section');
|
||||
this.make_customer_selector();
|
||||
}
|
||||
|
||||
reset_customer_selector() {
|
||||
@ -302,7 +305,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.$customer_section.html(`<div class="customer-search-field flex flex-1 items-center"></div>`);
|
||||
const me = this;
|
||||
const query = { query: 'erpnext.controllers.queries.customer_query' };
|
||||
const allowed_customer_group = this.events.get_allowed_customer_group() || [];
|
||||
const allowed_customer_group = this.allowed_customer_groups || [];
|
||||
if (allowed_customer_group.length) {
|
||||
query.filters = {
|
||||
customer_group: ['in', allowed_customer_group]
|
||||
@ -423,6 +426,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
update_customer_section() {
|
||||
const me = this;
|
||||
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
||||
|
||||
if (customer) {
|
||||
@ -460,7 +464,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
function get_customer_image() {
|
||||
if (image) {
|
||||
if (!me.hide_images && image) {
|
||||
return `<div class="icon flex items-center justify-center w-12 h-12 rounded bg-light-grey mr-4 text-grey-200">
|
||||
<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">
|
||||
</div>`
|
||||
|
@ -1,8 +1,10 @@
|
||||
erpnext.PointOfSale.ItemSelector = class {
|
||||
constructor({ frm, wrapper, events, pos_profile }) {
|
||||
constructor({ frm, wrapper, events, pos_profile, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.pos_profile = pos_profile;
|
||||
this.hide_images = settings.hide_images;
|
||||
this.auto_add_item = settings.auto_add_item_to_cart;
|
||||
|
||||
this.inti_component();
|
||||
}
|
||||
@ -26,13 +28,14 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
<div class="flex flex-1 flex-col p-8 pt-2">
|
||||
<div class="text-grey mb-6">ALL ITEMS</div>
|
||||
<div class="items-container grid grid-cols-4 gap-8">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>`
|
||||
);
|
||||
|
||||
this.$component = this.wrapper.find('.items-selector');
|
||||
this.$items_container = this.$component.find('.items-container');
|
||||
}
|
||||
|
||||
async load_items_data() {
|
||||
@ -65,7 +68,6 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
|
||||
render_item_list(items) {
|
||||
this.$items_container = this.$component.find('.items-container');
|
||||
this.$items_container.html('');
|
||||
|
||||
items.forEach(item => {
|
||||
@ -75,11 +77,12 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
|
||||
get_item_html(item) {
|
||||
const me = this;
|
||||
const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
|
||||
const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
|
||||
|
||||
function get_item_image_html() {
|
||||
if (item_image) {
|
||||
if (!me.hide_images && item_image) {
|
||||
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
|
||||
<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
|
||||
</div>`
|
||||
@ -203,6 +206,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
ignore_inputs: true,
|
||||
page: cur_page.page.page
|
||||
});
|
||||
|
||||
// for selecting the last filtered item on search
|
||||
frappe.ui.keys.on("enter", () => {
|
||||
const selector_is_visible = this.$component.is(':visible');
|
||||
@ -235,6 +239,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
const items = this.search_index[search_term];
|
||||
this.items = items;
|
||||
this.render_item_list(items);
|
||||
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -247,8 +252,13 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
this.items = items;
|
||||
this.render_item_list(items);
|
||||
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||
});
|
||||
}
|
||||
|
||||
add_filtered_item_to_cart() {
|
||||
this.$items_container.find(".item-wrapper").click();
|
||||
}
|
||||
|
||||
resize_selector(minimize) {
|
||||
minimize ?
|
||||
|
@ -10,8 +10,8 @@ from frappe.utils.nestedset import get_descendants_of
|
||||
def execute(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_('From Date cannot be greater than To Date'))
|
||||
|
||||
frappe.throw(_("From Date cannot be greater than To Date"))
|
||||
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
|
||||
@ -148,14 +148,16 @@ def get_data(filters):
|
||||
company_list.append(filters.get("company"))
|
||||
|
||||
customer_details = get_customer_details()
|
||||
item_details = get_item_details()
|
||||
sales_order_records = get_sales_order_details(company_list, filters)
|
||||
|
||||
for record in sales_order_records:
|
||||
customer_record = customer_details.get(record.customer)
|
||||
item_record = item_details.get(record.item_code)
|
||||
row = {
|
||||
"item_code": record.item_code,
|
||||
"item_name": record.item_name,
|
||||
"item_group": record.item_group,
|
||||
"item_name": item_record.item_name,
|
||||
"item_group": item_record.item_group,
|
||||
"description": record.description,
|
||||
"quantity": record.qty,
|
||||
"uom": record.uom,
|
||||
@ -196,8 +198,8 @@ def get_conditions(filters):
|
||||
return conditions
|
||||
|
||||
def get_customer_details():
|
||||
details = frappe.get_all('Customer',
|
||||
fields=['name', 'customer_name', "customer_group"])
|
||||
details = frappe.get_all("Customer",
|
||||
fields=["name", "customer_name", "customer_group"])
|
||||
customer_details = {}
|
||||
for d in details:
|
||||
customer_details.setdefault(d.name, frappe._dict({
|
||||
@ -206,15 +208,25 @@ def get_customer_details():
|
||||
}))
|
||||
return customer_details
|
||||
|
||||
def get_item_details():
|
||||
details = frappe.db.get_all("Item",
|
||||
fields=["item_code", "item_name", "item_group"])
|
||||
item_details = {}
|
||||
for d in details:
|
||||
item_details.setdefault(d.item_code, frappe._dict({
|
||||
"item_name": d.item_name,
|
||||
"item_group": d.item_group
|
||||
}))
|
||||
return item_details
|
||||
|
||||
def get_sales_order_details(company_list, filters):
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
so_item.item_code, so_item.item_name, so_item.item_group,
|
||||
so_item.description, so_item.qty, so_item.uom,
|
||||
so_item.base_rate, so_item.base_amount, so.name,
|
||||
so.transaction_date, so.customer, so.territory,
|
||||
so_item.item_code, so_item.description, so_item.qty,
|
||||
so_item.uom, so_item.base_rate, so_item.base_amount,
|
||||
so.name, so.transaction_date, so.customer,so.territory,
|
||||
so.project, so_item.delivered_qty,
|
||||
so_item.billed_amt, so.company
|
||||
FROM
|
||||
|
@ -274,7 +274,8 @@ erpnext.company.setup_queries = function(frm) {
|
||||
["default_employee_advance_account", {"root_type": "Asset"}],
|
||||
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
||||
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
||||
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}]
|
||||
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
|
||||
["unrealized_profit_loss_account", {"root_type": "Liability"}]
|
||||
], function(i, v) {
|
||||
erpnext.company.set_custom_query(frm, v);
|
||||
});
|
||||
|
@ -46,10 +46,9 @@
|
||||
"round_off_account",
|
||||
"round_off_cost_center",
|
||||
"write_off_account",
|
||||
"discount_allowed_account",
|
||||
"discount_received_account",
|
||||
"exchange_gain_loss_account",
|
||||
"unrealized_exchange_gain_loss_account",
|
||||
"unrealized_profit_loss_account",
|
||||
"column_break0",
|
||||
"allow_account_creation_against_child_company",
|
||||
"default_payable_account",
|
||||
@ -261,14 +260,14 @@
|
||||
{
|
||||
"fieldname": "create_chart_of_accounts_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Create Chart of Accounts Based on",
|
||||
"label": "Create Chart Of Accounts Based On",
|
||||
"options": "\nStandard Template\nExisting Company"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
|
||||
"fieldname": "chart_of_accounts",
|
||||
"fieldtype": "Select",
|
||||
"label": "Chart of Accounts Template",
|
||||
"label": "Chart Of Accounts Template",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
@ -345,18 +344,6 @@
|
||||
"label": "Write Off Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "discount_allowed_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Allowed Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "discount_received_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Received Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "exchange_gain_loss_account",
|
||||
"fieldtype": "Link",
|
||||
@ -740,6 +727,12 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Default In Transit Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Unrealized Profit / Loss Account",
|
||||
"options": "Account"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-building",
|
||||
@ -747,7 +740,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-06 00:38:08.311216",
|
||||
"modified": "2020-12-03 12:27:27.085094",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
@ -808,4 +801,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
@ -845,6 +845,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
},
|
||||
|
||||
fg_completed_qty: function() {
|
||||
this.get_items();
|
||||
},
|
||||
|
||||
get_items: function() {
|
||||
var me = this;
|
||||
if(!this.frm.doc.fg_completed_qty || !this.frm.doc.bom_no)
|
||||
@ -854,6 +858,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
// if work order / bom is mentioned, get items
|
||||
return this.frm.call({
|
||||
doc: me.frm.doc,
|
||||
freeze: true,
|
||||
method: "get_items",
|
||||
callback: function(r) {
|
||||
if(!r.exc) refresh_field("items");
|
||||
|
@ -129,6 +129,7 @@ class StockEntry(StockController):
|
||||
self.update_transferred_qty()
|
||||
self.update_quality_inspection()
|
||||
self.delete_auto_created_batches()
|
||||
self.delete_linked_stock_entry()
|
||||
|
||||
if self.purpose == 'Material Transfer' and self.add_to_transit:
|
||||
self.set_material_request_transfer_status('Not Started')
|
||||
@ -161,6 +162,12 @@ class StockEntry(StockController):
|
||||
frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
|
||||
.format(self.job_card))
|
||||
|
||||
def delete_linked_stock_entry(self):
|
||||
if self.purpose == "Send to Warehouse":
|
||||
for d in frappe.get_all("Stock Entry", filters={"docstatus": 0,
|
||||
"outgoing_stock_entry": self.name, "purpose": "Receive at Warehouse"}):
|
||||
frappe.delete_doc("Stock Entry", d.name)
|
||||
|
||||
def set_transfer_qty(self):
|
||||
for item in self.get("items"):
|
||||
if not flt(item.qty):
|
||||
@ -1042,26 +1049,22 @@ class StockEntry(StockController):
|
||||
wo = frappe.get_doc("Work Order", self.work_order)
|
||||
wo_items = frappe.get_all('Work Order Item',
|
||||
filters={'parent': self.work_order},
|
||||
fields=["item_code", "required_qty", "consumed_qty"]
|
||||
fields=["item_code", "required_qty", "consumed_qty", "transferred_qty"]
|
||||
)
|
||||
|
||||
work_order_qty = wo.material_transferred_for_manufacturing or wo.qty
|
||||
for item in wo_items:
|
||||
qty = item.required_qty
|
||||
|
||||
item_account_details = get_item_defaults(item.item_code, self.company)
|
||||
# Take into account consumption if there are any.
|
||||
if self.purpose == 'Manufacture':
|
||||
req_qty_each = flt(item.required_qty / wo.qty)
|
||||
if (flt(item.consumed_qty) != 0):
|
||||
remaining_qty = flt(item.consumed_qty) - (flt(wo.produced_qty) * req_qty_each)
|
||||
exhaust_qty = req_qty_each * wo.produced_qty
|
||||
if remaining_qty > exhaust_qty :
|
||||
if (remaining_qty/(req_qty_each * flt(self.fg_completed_qty))) >= 1:
|
||||
qty =0
|
||||
else:
|
||||
qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty
|
||||
else:
|
||||
qty = req_qty_each * flt(self.fg_completed_qty)
|
||||
|
||||
wo_item_qty = item.transferred_qty or item.required_qty
|
||||
|
||||
req_qty_each = (
|
||||
(flt(wo_item_qty) - flt(item.consumed_qty)) /
|
||||
(flt(work_order_qty) - flt(wo.produced_qty))
|
||||
)
|
||||
|
||||
qty = req_qty_each * flt(self.fg_completed_qty)
|
||||
|
||||
if qty > 0:
|
||||
self.add_to_stock_entry_detail({
|
||||
@ -1143,13 +1146,15 @@ class StockEntry(StockController):
|
||||
else:
|
||||
qty = req_qty_each * flt(self.fg_completed_qty)
|
||||
|
||||
|
||||
elif backflushed_materials.get(item.item_code):
|
||||
for d in backflushed_materials.get(item.item_code):
|
||||
if d.get(item.warehouse):
|
||||
if (qty > req_qty):
|
||||
qty = (qty/trans_qty) * flt(self.fg_completed_qty)
|
||||
|
||||
if consumed_qty:
|
||||
qty -= consumed_qty
|
||||
|
||||
if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')):
|
||||
qty = frappe.utils.ceil(qty)
|
||||
|
||||
|
@ -20,10 +20,10 @@
|
||||
{%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ charge.get_formatted("description") }}</label></div>
|
||||
<label>{{ charge.get_formatted("description") }}</label>
|
||||
</div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ frappe.format_value(frappe.utils.flt(charge.tax_amount),
|
||||
table_meta.get_field("tax_amount"), doc, currency=doc.currency) }}
|
||||
{{ charge.get_formatted('tax_amount', doc) }}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
Loading…
x
Reference in New Issue
Block a user