Merge branch 'develop' into fix-remaining-leave-balance-calculation

This commit is contained in:
Anurag Mishra 2020-01-20 19:41:33 +05:30 committed by GitHub
commit 85e5ba9698
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 806 additions and 2007 deletions

File diff suppressed because it is too large Load Diff

View File

@ -373,6 +373,7 @@ def get_existing_payment_request_amount(ref_dt, ref_dn):
reference_doctype = %s
and reference_name = %s
and docstatus = 1
and status != 'Paid'
""", (ref_dt, ref_dn))
return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0

View File

@ -167,8 +167,15 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
make_comment_dialog_and_block_invoice: function(){
const me = this;
const title = __('Add Comment');
const title = __('Block Invoice');
const fields = [
{
fieldname: 'release_date',
read_only: 0,
fieldtype:'Date',
label: __('Release Date'),
default: me.frm.doc.release_date
},
{
fieldname: 'hold_comment',
read_only: 0,
@ -187,7 +194,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
const dialog_data = me.dialog.get_values();
frappe.call({
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.block_invoice',
'args': {'name': me.frm.doc.name, 'hold_comment': dialog_data.hold_comment},
'args': {
'name': me.frm.doc.name,
'hold_comment': dialog_data.hold_comment,
'release_date': dialog_data.release_date
},
'callback': (r) => me.frm.reload_doc()
});
me.dialog.hide();

View File

@ -927,9 +927,10 @@ class PurchaseInvoice(BuyingController):
def on_recurring(self, reference_doc, auto_repeat_doc):
self.due_date = None
def block_invoice(self, hold_comment=None):
def block_invoice(self, hold_comment=None, release_date=None):
self.db_set('on_hold', 1)
self.db_set('hold_comment', cstr(hold_comment))
self.db_set('release_date', release_date)
def unblock_invoice(self):
self.db_set('on_hold', 0)
@ -1013,10 +1014,10 @@ def unblock_invoice(name):
@frappe.whitelist()
def block_invoice(name, hold_comment):
def block_invoice(name, hold_comment, release_date):
if frappe.db.exists('Purchase Invoice', name):
pi = frappe.get_doc('Purchase Invoice', name)
pi.block_invoice(hold_comment)
pi.block_invoice(hold_comment, release_date)
@frappe.whitelist()
def make_inter_company_sales_invoice(source_name, target_doc=None):

View File

@ -1,134 +1,134 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2018-11-22 23:38:39.668804",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2018-11-22 23:38:39.668804",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-22 23:38:39.668804",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Category",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2020-01-15 17:14:28.951793",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Category",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
}

View File

@ -22,7 +22,7 @@ def post_depreciation_entries(date=None):
def get_depreciable_assets(date):
return frappe.db.sql_list("""select a.name
from tabAsset a, `tabDepreciation Schedule` ds
where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s
where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s and a.calculate_depreciation = 1
and a.status in ('Submitted', 'Partially Depreciated')
and ifnull(ds.journal_entry, '')=''""", date)

View File

@ -1,5 +1,4 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-21 16:16:39",
@ -48,7 +47,6 @@
"ignore_pricing_rule",
"sec_warehouse",
"set_warehouse",
"set_reserve_warehouse",
"col_break_warehouse",
"is_subcontracted",
"supplier_warehouse",
@ -58,6 +56,7 @@
"section_break_48",
"pricing_rules",
"raw_material_details",
"set_reserve_warehouse",
"supplied_items",
"sb_last_purchase",
"total_qty",
@ -1054,8 +1053,7 @@
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2019-12-30 19:11:54.122264",
"modified": "2020-01-14 18:54:39.694448",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -182,7 +182,7 @@ class StatusUpdater(Document):
if args.get('no_allowance'):
item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']]
if item['reduce_by'] > .01:
self.limits_crossed_error(args, item)
self.limits_crossed_error(args, item, "qty")
elif item[args['target_ref_field']]:
self.check_overflow_with_allowance(item, args)

View File

@ -66,6 +66,7 @@ class StockController(AccountsController):
gl_list = []
warehouse_with_no_account = []
precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
if sle_list:
@ -91,7 +92,7 @@ class StockController(AccountsController):
"against": item_row.expense_account,
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2),
"debit": flt(sle.stock_value_difference, precision),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
@ -101,7 +102,7 @@ class StockController(AccountsController):
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
"credit": flt(sle.stock_value_difference, precision),
"project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
}, item=item_row))

View File

@ -307,7 +307,7 @@ def set_multiple_status(names, status):
def auto_close_opportunity():
""" auto close the `Replied` Opportunities after 7 days """
auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_opportunity_after_days") or 15
auto_close_after_days = frappe.db.get_single_value("Selling Settings", "close_opportunity_after_days") or 15
opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and
modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)

View File

@ -53,7 +53,11 @@ def add_institution(token, response):
@frappe.whitelist()
def add_bank_accounts(response, bank, company):
response = json.loads(response) if not "accounts" in response else response
try:
response = json.loads(response)
except TypeError:
pass
bank = json.loads(bank)
result = []

View File

@ -264,6 +264,13 @@ doc_events = {
}
}
# On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled.
# to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled.
# if payment entry not in auto cancel exempted doctypes it will cancel payment entry.
auto_cancel_exempted_doctypes= [
"Payment Entry"
]
scheduler_events = {
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder"

View File

@ -265,7 +265,7 @@
"bold": 1,
"fieldname": "person_to_be_contacted",
"fieldtype": "Data",
"label": "Emergency Contact"
"label": "Emergency Contact Name"
},
{
"fieldname": "relation",
@ -787,7 +787,7 @@
"idx": 24,
"image_field": "image",
"links": [],
"modified": "2019-12-11 13:22:18.323622",
"modified": "2020-01-09 04:23:55.611366",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@ -47,7 +47,7 @@ frappe.ui.form.on('Employee Advance', {
}
if (frm.doc.docstatus === 1
&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount))
&& (flt(frm.doc.claimed_amount) + flt(frm.doc.return_amount) < flt(frm.doc.paid_amount))
&& frappe.model.can_create("Journal Entry")) {
frm.add_custom_button(__("Return"), function() {
@ -96,12 +96,12 @@ frappe.ui.form.on('Employee Advance', {
frappe.call({
method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry',
args: {
'employee_name': frm.doc.employee,
'employee': frm.doc.employee,
'company': frm.doc.company,
'employee_advance_name': frm.doc.name,
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
'mode_of_payment': frm.doc.mode_of_payment,
'advance_account': frm.doc.advance_account
'advance_account': frm.doc.advance_account,
'mode_of_payment': frm.doc.mode_of_payment
},
callback: function(r) {
const doclist = frappe.model.sync(r.message);

View File

@ -133,7 +133,8 @@ def make_bank_entry(dt, dn):
return je.as_dict()
@frappe.whitelist()
def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account):
def make_return_entry(employee, company, employee_advance_name,
return_amount, advance_account, mode_of_payment=None):
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
je = frappe.new_doc('Journal Entry')
je.posting_date = nowdate()
@ -147,7 +148,7 @@ def make_return_entry(employee_name, company, employee_advance_name, return_amou
'reference_type': 'Employee Advance',
'reference_name': employee_advance_name,
'party_type': 'Employee',
'party': employee_name,
'party': employee,
'is_advance': 'Yes'
})
@ -159,5 +160,5 @@ def make_return_entry(employee_name, company, employee_advance_name, return_amou
})
return je.as_dict()

View File

@ -242,13 +242,14 @@ frappe.ui.form.on("Expense Claim", {
},
update_employee_advance_claimed_amount: function(frm) {
console.log("update_employee_advance_claimed_amount")
let amount_to_be_allocated = frm.doc.grand_total;
$.each(frm.doc.advances || [], function(i, advance) {
if (amount_to_be_allocated >= advance.unclaimed_amount) {
frm.doc.advances[i].allocated_amount = frm.doc.advances[i].unclaimed_amount;
$.each(frm.doc.advances || [], function(i, advance){
if (amount_to_be_allocated >= advance.unclaimed_amount){
advance.allocated_amount = frm.doc.advances[i].unclaimed_amount;
amount_to_be_allocated -= advance.allocated_amount;
} else {
frm.doc.advances[i].allocated_amount = amount_to_be_allocated;
advance.allocated_amount = amount_to_be_allocated;
amount_to_be_allocated = 0;
}
frm.refresh_field("advances");
@ -300,6 +301,7 @@ frappe.ui.form.on("Expense Claim", {
doc: frm.doc,
callback: () => {
refresh_field("taxes");
frm.trigger("update_employee_advance_claimed_amount");
}
});
}
@ -340,16 +342,12 @@ frappe.ui.form.on("Expense Claim Detail", {
},
amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
var doc = frm.doc;
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount);
cur_frm.cscript.calculate_total(doc,cdt,cdn);
},
sanctioned_amount: function(frm, cdt, cdn) {
var doc = frm.doc;
cur_frm.cscript.calculate_total(doc,cdt,cdn);
cur_frm.cscript.calculate_total(frm.doc, cdt, cdn);
frm.trigger("get_taxes");
frm.trigger("calculate_grand_total");
},
cost_center: function(frm, cdt, cdn) {
erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center");

View File

@ -244,6 +244,7 @@ class ExpenseClaim(AccountsController):
precision = self.precision("total_advance_amount")
if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total claimed amount"))
if self.total_sanctioned_amount \
and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision):
frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount"))

View File

@ -1,5 +1,4 @@
{
"actions": [],
"creation": "2017-10-09 16:53:26.410762",
"doctype": "DocType",
"document_type": "Document",
@ -43,7 +42,7 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Advance Paid",
"options": "Company:company.default_currency",
"options": "Company:company:default_currency",
"read_only": 1
},
{
@ -55,7 +54,7 @@
"no_copy": 1,
"oldfieldname": "advance_amount",
"oldfieldtype": "Currency",
"options": "Company:company.default_currency",
"options": "Company:company:default_currency",
"print_width": "120px",
"read_only": 1,
"reqd": 1,
@ -70,7 +69,7 @@
"no_copy": 1,
"oldfieldname": "allocated_amount",
"oldfieldtype": "Currency",
"options": "Company:company.default_currency",
"options": "Company:company:default_currency",
"print_width": "120px",
"width": "120px"
},
@ -88,7 +87,7 @@
],
"istable": 1,
"links": [],
"modified": "2019-12-11 13:53:22.111766",
"modified": "2019-12-17 13:53:22.111766",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Advance",

View File

@ -75,6 +75,7 @@ frappe.ui.form.on('Salary Structure', {
title: __("Assign to Employees"),
fields: [
{fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")},
{fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1},
{fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")},
{fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')},
{fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')},
@ -87,7 +88,6 @@ frappe.ui.form.on('Salary Structure', {
],
primary_action: function() {
var data = d.get_values();
frappe.call({
doc: frm.doc,
method: "assign_salary_structure",

View File

@ -81,24 +81,24 @@ class SalaryStructure(Document):
return employees
@frappe.whitelist()
def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None,
def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None,
from_date=None, base=None,variable=None):
employees = self.get_employees(grade= grade,department= department,designation= designation,name=employee)
employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee)
if employees:
if len(employees) > 20:
frappe.enqueue(assign_salary_structure_for_employees, timeout=600,
employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable)
else:
assign_salary_structure_for_employees(employees, self,from_date=from_date, base=base,variable=variable)
assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable)
else:
frappe.msgprint(_("No Employee Found"))
def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None):
def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None):
salary_structures_assignments = []
existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date)
existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date)
count=0
for employee in employees:
if employee in existing_assignments_for:
@ -117,6 +117,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b
assignment = frappe.new_doc("Salary Structure Assignment")
assignment.employee = employee
assignment.salary_structure = salary_structure.name
assignment.company = salary_structure.company
assignment.from_date = from_date
assignment.base = base
assignment.variable = variable
@ -125,12 +126,12 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b
return assignment.name
def get_existing_assignments(employees, salary_structure,from_date):
def get_existing_assignments(employees, salary_structure, from_date):
salary_structures_assignments = frappe.db.sql_list("""
select distinct employee from `tabSalary Structure Assignment`
where salary_structure=%s and employee in (%s)
and from_date=%s and docstatus=1
""" % ('%s', ', '.join(['%s']*len(employees)),'%s'), [salary_structure] + employees+[from_date])
and from_date=%s and company= %s and docstatus=1
""" % ('%s', ', '.join(['%s']*len(employees)),'%s', '%s'), [salary_structure.name] + employees+[from_date]+[salary_structure.company])
if salary_structures_assignments:
frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}")
.format("\n".join(salary_structures_assignments)))
@ -170,7 +171,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
def get_employees(salary_structure):
employees = frappe.get_list('Salary Structure Assignment',
filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee'])
if not employees:
frappe.throw(_("There's no Employee with Salary Structure: {0}. \
Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure))

View File

@ -588,7 +588,7 @@ class BOM(WebsiteGenerator):
for d in self.operations:
if not d.description:
d.description = frappe.db.get_value('Operation', d.operation, 'description')
if not d.batch_size > 0:
if not d.batch_size or d.batch_size <= 0:
d.batch_size = 1
def get_list_context(context):
@ -634,7 +634,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
is_stock_item=is_stock_item,
qty_field="stock_qty",
select_columns = """, bom_item.source_warehouse, bom_item.operation,
bom_item.include_item_in_manufacturing, bom_item.description,
bom_item.include_item_in_manufacturing, bom_item.description, bom_item.rate,
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""")
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
@ -648,7 +648,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty",
select_columns = """, bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse,
bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing,
bom_item.description """)
bom_item.description, bom_item.base_rate as rate """)
items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True)
for item in items:
@ -761,11 +761,17 @@ def get_boms_in_bottom_up_order(bom_no=None):
def add_additional_cost(stock_entry, work_order):
# Add non stock items cost in the additional cost
bom = frappe.get_doc('BOM', work_order.bom_no)
table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
stock_entry.additional_costs = []
expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company,
"expenses_included_in_valuation")
add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation)
add_operations_cost(stock_entry, work_order, expenses_included_in_valuation)
def add_non_stock_items_cost(stock_entry, work_order, expense_account):
bom = frappe.get_doc('BOM', work_order.bom_no)
table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
items = {}
for d in bom.get(table):
items.setdefault(d.item_code, d.amount)
@ -773,13 +779,39 @@ def add_additional_cost(stock_entry, work_order):
non_stock_items = frappe.get_all('Item',
fields="name", filters={'name': ('in', list(items.keys())), 'ifnull(is_stock_item, 0)': 0}, as_list=1)
non_stock_items_cost = 0.0
for name in non_stock_items:
non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity)
if non_stock_items_cost:
stock_entry.append('additional_costs', {
'expense_account': expenses_included_in_valuation,
'description': name[0],
'amount': flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity)
'expense_account': expense_account,
'description': _("Non stock items"),
'amount': non_stock_items_cost
})
def add_operations_cost(stock_entry, work_order=None, expense_account=None):
from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit
operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no)
if operating_cost_per_unit:
stock_entry.append('additional_costs', {
"expense_account": expense_account,
"description": _("Operating Cost as per Work Order / BOM"),
"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
})
if work_order and work_order.additional_operating_cost and work_order.qty:
additional_operating_cost_per_unit = \
flt(work_order.additional_operating_cost) / flt(work_order.qty)
if additional_operating_cost_per_unit:
stock_entry.append('additional_costs', {
"expense_account": expense_account,
"description": "Additional Operating Cost",
"amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty)
})
@frappe.whitelist()
def get_bom_diff(bom1, bom2):
from frappe.model import table_fields

View File

@ -12,8 +12,7 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError
from erpnext.manufacturing.doctype.job_card.job_card import OverlapError
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from frappe.utils.csvutils import getlink
@ -278,10 +277,11 @@ class WorkOrder(Document):
enable_capacity_planning=enable_capacity_planning, auto_create=True)
if enable_capacity_planning and job_card_doc:
row.planned_start_time = job_card_doc.time_logs[0].from_time
row.planned_start_time = job_card_doc.time_logs[-1].from_time
row.planned_end_time = job_card_doc.time_logs[-1].to_time
if date_diff(row.planned_start_time, original_start_time) > plan_days:
frappe.message_log.pop()
frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.")
.format(plan_days, row.operation), CapacityError)
@ -708,10 +708,6 @@ def make_stock_entry(work_order_id, purpose, qty=None):
stock_entry.from_warehouse = wip_warehouse
stock_entry.to_warehouse = work_order.fg_warehouse
stock_entry.project = work_order.project
if purpose=="Manufacture":
additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty,
company=work_order.company)
stock_entry.set("additional_costs", additional_costs)
stock_entry.set_stock_entry_type()
stock_entry.get_items()

View File

@ -13,6 +13,7 @@ erpnext.patches.v4_0.apply_user_permissions
erpnext.patches.v4_0.move_warehouse_user_to_restrictions
erpnext.patches.v4_0.global_defaults_to_system_settings
erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule
execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16
execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24
execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31
execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29
@ -513,7 +514,6 @@ erpnext.patches.v11_0.rename_employee_loan_to_loan
erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018
erpnext.patches.v11_0.update_department_lft_rgt
erpnext.patches.v11_0.add_default_email_template_for_leave
execute:frappe.reload_doc("HR", "doctype", "HR Settings")
erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
erpnext.patches.v11_0.uom_conversion_data #30-06-2018
erpnext.patches.v10_0.taxes_issue_with_pos

View File

@ -49,12 +49,13 @@ def execute():
item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
# update the item tax table
item = frappe.get_doc("Item", item_code)
item.set("taxes", [])
item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""})
frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
for d in item.taxes:
d.db_insert()
if item_tax_template_name:
item = frappe.get_doc("Item", item_code)
item.set("taxes", [])
item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""})
for d in item.taxes:
d.db_insert()
doctypes = [
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
@ -95,30 +96,35 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp
else:
parts = tax_type.strip().split(" - ")
account_name = " - ".join(parts[:-1])
company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
if not parent_account:
if not account_name:
tax_type = None
else:
company = get_company(parts[-1], parenttype, parent)
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company})
filters = {
"account_name": account_name,
"company": company,
"account_type": "Tax",
"parent_account": parent_account
}
tax_type = frappe.db.get_value("Account", filters)
if not tax_type:
account = frappe.new_doc("Account")
account.update(filters)
account.insert()
tax_type = account.name
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account")
if not parent_account:
parent_account = frappe.db.get_value("Account",
filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company})
filters = {
"account_name": account_name,
"company": company,
"account_type": "Tax",
"parent_account": parent_account
}
tax_type = frappe.db.get_value("Account", filters)
if not tax_type:
account = frappe.new_doc("Account")
account.update(filters)
account.insert()
tax_type = account.name
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
item_tax_templates.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
item_tax_template.save()
return item_tax_template.name
if tax_type:
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
item_tax_templates.setdefault(item_tax_template.title, {})
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
if item_tax_template.get("taxes"):
item_tax_template.save()
return item_tax_template.name
def get_company(company_abbr, parenttype=None, parent=None):
if parenttype and parent:

View File

@ -13,6 +13,8 @@ def execute():
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category')
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail')
frappe.reload_doc('accounts', 'doctype', 'tax_category')
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype)
@ -29,4 +31,4 @@ def execute():
update tabAddress
set gst_state_number=concat("0", gst_state_number)
where ifnull(gst_state_number, '') != '' and gst_state_number<10
""")
""")

View File

@ -240,7 +240,7 @@ def create_supplier(supplier_group, args):
if not frappe.get_list("Contact", filters):
new_contact = frappe.new_doc("Contact")
new_contact.first_name = args.supplier
new_contact.first_name = args.supplier[:30]
new_contact.append('links', {
"link_doctype": "Supplier",
"link_name": existing_supplier_name
@ -251,7 +251,7 @@ def create_supplier(supplier_group, args):
else:
new_supplier = frappe.new_doc("Supplier")
new_supplier.supplier_name = args.supplier
new_supplier.supplier_name = re.sub('&amp', '&', args.supplier)
new_supplier.supplier_group = supplier_group
new_supplier.tax_id = args.tax_id
new_supplier.fiscal_code = args.fiscal_code
@ -259,7 +259,7 @@ def create_supplier(supplier_group, args):
new_supplier.save()
new_contact = frappe.new_doc("Contact")
new_contact.first_name = args.supplier
new_contact.first_name = args.supplier[:30]
new_contact.append('links', {
"link_doctype": "Supplier",
"link_name": new_supplier.name

View File

@ -103,7 +103,7 @@ frappe.ui.form.on("Customer", {
}
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
frm.toggle_display(['address_html','contact_html','primary_address_and_contact_detail'], !frm.doc.__islocal);
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if(!frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(frm);

View File

@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "hash",
"creation": "2013-03-07 11:42:58",
"doctype": "DocType",
@ -151,6 +152,7 @@
"width": "300px"
},
{
"allow_on_submit": 1,
"columns": 2,
"depends_on": "eval: !parent.skip_delivery_note",
"fieldname": "delivery_date",
@ -767,7 +769,8 @@
],
"idx": 1,
"istable": 1,
"modified": "2019-12-12 18:06:26.238169",
"links": [],
"modified": "2020-01-13 12:29:03.103797",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",

View File

@ -76,7 +76,7 @@ class AuthorizationControl(TransactionBase):
add_cond = ''
auth_value = av_dis
if val == 1: add_cond += " and system_user = '"+ frappe.db.escape(session['user'])+"'"
if val == 1: add_cond += " and system_user = {}".format(frappe.db.escape(session['user']))
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')")
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
@ -85,7 +85,7 @@ class AuthorizationControl(TransactionBase):
if doc_obj:
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
else: customer = doc_obj.customer_name
add_cond = " and master_name = '"+ frappe.db.escape(customer) +"'"
add_cond = " and master_name = {}".format(frappe.db.escape(customer))
if based_on == 'Itemwise Discount':
if doc_obj:
for t in doc_obj.get("items"):

View File

@ -436,7 +436,7 @@ def make_sales_invoice(source_name, target_doc=None):
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = to_make_invoice_qty_map[source_doc.name]
if source_doc.serial_no and source_parent.per_billed > 0:
if source_doc.serial_no and source_parent.per_billed > 0 and not source_parent.is_return:
target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code,
target_doc.qty, source_parent.name)

View File

@ -13,7 +13,7 @@ from frappe.model.document import Document
class ItemPrice(Document):
def validate(self):
self.validate_item()
self.validate_dates()
@ -51,7 +51,7 @@ class ItemPrice(Document):
def check_duplicates(self):
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
for field in ['uom', 'min_qty', 'valid_from',
for field in ['uom', 'valid_from',
'valid_upto', 'packing_unit', 'customer', 'supplier']:
if self.get(field):
conditions += " and {0} = %({1})s".format(field, field)

View File

@ -21,7 +21,7 @@ class TestItemPrice(unittest.TestCase):
def test_addition_of_new_fields(self):
# Based on https://github.com/frappe/erpnext/issues/8456
test_fields_existance = [
'supplier', 'customer', 'uom', 'min_qty', 'lead_time_days',
'supplier', 'customer', 'uom', 'lead_time_days',
'packing_unit', 'valid_from', 'valid_upto', 'note'
]
doc_fields = frappe.copy_doc(test_records[1]).__dict__.keys()
@ -43,7 +43,6 @@ class TestItemPrice(unittest.TestCase):
args = {
"price_list": doc.price_list,
"min_qty": doc.min_qty,
"customer": doc.customer,
"uom": "_Test UOM",
"transaction_date": '2017-04-18',
@ -58,7 +57,6 @@ class TestItemPrice(unittest.TestCase):
doc = frappe.copy_doc(test_records[2])
args = {
"price_list": doc.price_list,
"min_qty": 30,
"customer": doc.customer,
"uom": "_Test UOM",
"transaction_date": '2017-04-18',
@ -74,7 +72,6 @@ class TestItemPrice(unittest.TestCase):
args = {
"price_list": doc.price_list,
"min_qty": doc.min_qty,
"customer": "_Test Customer",
"uom": "_Test UOM",
"transaction_date": '2017-04-18',
@ -90,7 +87,6 @@ class TestItemPrice(unittest.TestCase):
args = {
"price_list": doc.price_list,
"min_qty": doc.min_qty,
"qty": 7,
"uom": "_Test UOM",
"transaction_date": "01-15-2019"
@ -105,7 +101,6 @@ class TestItemPrice(unittest.TestCase):
args = {
"price_list": doc.price_list,
"min_qty": doc.min_qty,
"customer": "_Test Customer",
"uom": "_Test UOM",
"transaction_date": "2017-04-25",
@ -121,7 +116,6 @@ class TestItemPrice(unittest.TestCase):
args = {
"price_list": doc.price_list,
"min_qty": doc.min_qty,
"uom": "_Test UOM",
"qty": 7,
}

View File

@ -230,8 +230,19 @@ frappe.ui.form.on('Material Request', {
make_purchase_order: function(frm) {
frappe.prompt(
{fieldname:'default_supplier', label: __('For Default Supplier (optional)'), description: __('Selected Supplier\
must be the Default Supplier of one of the items below.'), fieldtype: 'Link', options: 'Supplier'},
{
label: __('For Default Supplier (Optional)'),
fieldname:'default_supplier',
fieldtype: 'Link',
options: 'Supplier',
description: __('Select a Supplier from the Default Supplier List of the items below.'),
get_query: () => {
return{
query: "erpnext.stock.doctype.material_request.material_request.get_default_supplier_query",
filters: {'doc': frm.doc.name}
}
}
},
(values) => {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",

View File

@ -386,6 +386,18 @@ def get_material_requests_based_on_supplier(supplier):
return material_requests, supplier_items
def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters):
doc = frappe.get_doc("Material Request", filters.get("doc"))
item_list = []
for d in doc.items:
item_list.append(d.item_code)
return frappe.db.sql("""select default_supplier
from `tabItem Default`
where parent in ({0}) and
default_supplier IS NOT NULL
""".format(', '.join(['%s']*len(item_list))),tuple(item_list))
@frappe.whitelist()
def make_supplier_quotation(source_name, target_doc=None):
def postprocess(source, target_doc):

View File

@ -840,18 +840,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
if (me.frm.doc.purpose == "Manufacture" || me.frm.doc.purpose == "Material Consumption for Manufacture" ) {
if (me.frm.doc.purpose == "Manufacture") {
if (!me.frm.doc.to_warehouse) me.frm.set_value("to_warehouse", r.message["fg_warehouse"]);
if (r.message["additional_costs"].length) {
me.frm.clear_table("additional_costs");
$.each(r.message["additional_costs"], function(i, row) {
me.frm.add_child("additional_costs", row);
})
refresh_field("additional_costs");
}
}
if (!me.frm.doc.from_warehouse) me.frm.set_value("from_warehouse", r.message["wip_warehouse"]);
}
me.get_items()
me.get_items();
}
}
});

View File

@ -478,15 +478,17 @@ class StockEntry(StockController):
def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost):
if self.purpose in ["Manufacture", "Repack"]:
for d in self.get("items"):
if (d.transfer_qty and (d.bom_no or d.t_warehouse) and raw_material_cost
if (d.transfer_qty and (d.bom_no or d.t_warehouse)
and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)):
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
if (not d.basic_rate and self.work_order and
frappe.db.get_single_value("Manufacturing Settings", "material_consumption")):
d.basic_rate = get_valuation_rate_for_finished_good_entry(self.work_order) or 0
d.basic_amount = d.basic_rate * d.qty
if self.work_order \
and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"):
bom_items = self.get_bom_raw_materials(d.transfer_qty)
raw_material_cost = sum([flt(d.qty)*flt(d.rate) for d in bom_items.values()])
if raw_material_cost:
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
def distribute_additional_costs(self):
if self.purpose == "Material Issue":
@ -680,6 +682,8 @@ class StockEntry(StockController):
if item_account_wise_additional_cost:
for d in self.get("items"):
for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
if not amount: continue
gl_entries.append(self.get_gl_dict({
"account": account,
"against": d.expense_account,
@ -1399,34 +1403,9 @@ def get_work_order_details(work_order, company):
"use_multi_level_bom": work_order.use_multi_level_bom,
"wip_warehouse": work_order.wip_warehouse,
"fg_warehouse": work_order.fg_warehouse,
"fg_completed_qty": pending_qty_to_produce,
"additional_costs": get_additional_costs(work_order, fg_qty=pending_qty_to_produce, company=company)
"fg_completed_qty": pending_qty_to_produce
}
def get_additional_costs(work_order=None, bom_no=None, fg_qty=None, company=None):
additional_costs = []
operating_cost_per_unit = get_operating_cost_per_unit(work_order, bom_no)
expenses_included_in_valuation = frappe.get_cached_value("Company", company, "expenses_included_in_valuation")
if operating_cost_per_unit:
additional_costs.append({
"expense_account": expenses_included_in_valuation,
"description": "Operating Cost as per Work Order / BOM",
"amount": operating_cost_per_unit * flt(fg_qty)
})
if work_order and work_order.additional_operating_cost and work_order.qty:
additional_operating_cost_per_unit = \
flt(work_order.additional_operating_cost) / flt(work_order.qty)
additional_costs.append({
"expense_account": expenses_included_in_valuation,
"description": "Additional Operating Cost",
"amount": additional_operating_cost_per_unit * flt(fg_qty)
})
return additional_costs
def get_operating_cost_per_unit(work_order=None, bom_no=None):
operating_cost_per_unit = 0
if work_order:

View File

@ -583,7 +583,7 @@ def get_item_price(args, item_code, ignore_party=False):
Get name, price_list_rate from Item Price based on conditions
Check if the desired qty is within the increment of the packing list.
:param args: dict (or frappe._dict) with mandatory fields price_list, uom
optional fields min_qty, transaction_date, customer, supplier
optional fields transaction_date, customer, supplier
:param item_code: str, Item Doctype field item_code
"""
@ -601,24 +601,16 @@ def get_item_price(args, item_code, ignore_party=False):
else:
conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')"
if args.get('min_qty'):
conditions += " and ifnull(min_qty, 0) <= %(min_qty)s"
if args.get('transaction_date'):
conditions += """ and %(transaction_date)s between
ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
return frappe.db.sql(""" select name, price_list_rate, uom
from `tabItem Price` {conditions}
order by uom desc, min_qty desc, valid_from desc """.format(conditions=conditions), args)
order by valid_from desc, uom desc """.format(conditions=conditions), args)
def get_price_list_rate_for(args, item_code):
"""
Return Price Rate based on min_qty of each Item Price Rate.\
For example, desired qty is 10 and Item Price Rates exists
for min_qty 9 and min_qty 20. It returns Item Price Rate for qty 9 as
the best fit in the range of avaliable min_qtyies
:param customer: link to Customer DocType
:param supplier: link to Supplier DocType
:param price_list: str (Standard Buying or Standard Selling)
@ -632,7 +624,6 @@ def get_price_list_rate_for(args, item_code):
"customer": args.get('customer'),
"supplier": args.get('supplier'),
"uom": args.get('uom'),
"min_qty": args.get('qty'),
"transaction_date": args.get('transaction_date'),
}
@ -646,11 +637,8 @@ def get_price_list_rate_for(args, item_code):
for field in ["customer", "supplier"]:
del item_price_args[field]
general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
if not general_price_list_rate:
del item_price_args["min_qty"]
general_price_list_rate = get_item_price(item_price_args, item_code, ignore_party=args.get("ignore_party"))
general_price_list_rate = get_item_price(item_price_args, item_code,
ignore_party=args.get("ignore_party"))
if not general_price_list_rate and args.get("uom") != args.get("stock_uom"):
item_price_args["uom"] = args.get("stock_uom")

View File

@ -43,7 +43,7 @@ def get_data(report_filters):
def get_stock_ledger_data(report_filters, filters):
if report_filters.account:
warehouses = get_warehouses_based_on_account(report_filters.account,
report_filters.warehouse)
report_filters.company)
filters["warehouse"] = ("in", warehouses)

View File

@ -3432,7 +3432,7 @@ apps/erpnext/erpnext/accounts/doctype/bank_transaction/bank_transaction.py,The t
DocType: Landed Cost Voucher,Distribute Charges Based On,Kosten auf folgender Grundlage verteilen
DocType: Projects Settings,Timesheets,Zeiterfassungen
DocType: HR Settings,HR Settings,Einstellungen zum Modul Personalwesen
apps/erpnext/erpnext/config/accounting.py,Accounting Masters,Accounting Masters
apps/erpnext/erpnext/config/accounting.py,Accounting Masters,Buchhaltungsstammdaten
DocType: Salary Slip,net pay info,Netto-Zahlung Info
apps/erpnext/erpnext/regional/report/eway_bill/eway_bill.py,CESS Amount,CESS-Betrag
DocType: Woocommerce Settings,Enable Sync,Aktivieren Sie die Synchronisierung

Can't render this file because it is too large.