Merge branch 'develop' of https://github.com/frappe/erpnext into contacts-ref
This commit is contained in:
commit
1c538b9b43
@ -128,6 +128,7 @@ class Account(NestedSet):
|
||||
"account_currency": self.account_currency,
|
||||
"parent_account": parent_acc_name_map[company]
|
||||
})
|
||||
if not self.check_if_child_acc_exists(doc):
|
||||
doc.save()
|
||||
frappe.msgprint(_("Account {0} is added in the child company {1}")
|
||||
.format(doc.name, company))
|
||||
@ -172,6 +173,24 @@ class Account(NestedSet):
|
||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
||||
|
||||
def check_if_child_acc_exists(self, doc):
|
||||
''' Checks if a account in parent company exists in the '''
|
||||
info = frappe.db.get_value("Account", {
|
||||
"account_name": doc.account_name,
|
||||
"account_number": doc.account_number
|
||||
}, ['company', 'account_currency', 'is_group', 'root_type', 'account_type', 'balance_must_be', 'account_name'], as_dict=1)
|
||||
|
||||
if not info:
|
||||
return
|
||||
|
||||
doc = vars(doc)
|
||||
dict_diff = [k for k in info if k in doc and info[k] != doc[k] and k != "company"]
|
||||
if dict_diff:
|
||||
frappe.throw(_("Account {0} already exists in child company {1}. The following fields have different values, they should be same:<ul><li>{2}</li></ul>")
|
||||
.format(info.account_name, info.company, '</li><li>'.join(dict_diff)))
|
||||
else:
|
||||
return True
|
||||
|
||||
def convert_group_to_ledger(self):
|
||||
if self.check_if_child_exists():
|
||||
throw(_("Account with child nodes cannot be converted to ledger"))
|
||||
|
@ -1,29 +0,0 @@
|
||||
frappe.ui.form.on('Payment Order', {
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.docstatus==1 && frm.doc.payment_order_type==='Payment Entry') {
|
||||
frm.add_custom_button(__('Generate Text File'), function() {
|
||||
frm.trigger("generate_text_and_download_file");
|
||||
});
|
||||
}
|
||||
},
|
||||
generate_text_and_download_file: (frm) => {
|
||||
return frappe.call({
|
||||
method: "erpnext.regional.india.bank_remittance.generate_report",
|
||||
args: {
|
||||
name: frm.doc.name
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
{
|
||||
frm.reload_doc();
|
||||
const a = document.createElement('a');
|
||||
let file_obj = r.message;
|
||||
a.href = file_obj.file_url;
|
||||
a.target = '_blank';
|
||||
a.download = file_obj.file_name;
|
||||
a.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
|
||||
datasets = []
|
||||
if asset_data:
|
||||
datasets.append({'name':'Assets', 'values': asset_data})
|
||||
datasets.append({'name': _('Assets'), 'values': asset_data})
|
||||
if liability_data:
|
||||
datasets.append({'name':'Liabilities', 'values': liability_data})
|
||||
datasets.append({'name': _('Liabilities'), 'values': liability_data})
|
||||
if equity_data:
|
||||
datasets.append({'name':'Equity', 'values': equity_data})
|
||||
datasets.append({'name': _('Equity'), 'values': equity_data})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
|
@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
|
||||
fieldname:"payment_type",
|
||||
label: __("Payment Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Incoming\nOutgoing",
|
||||
default: "Incoming"
|
||||
options: __("Incoming") + "\n" + __("Outgoing"),
|
||||
default: __("Incoming")
|
||||
},
|
||||
{
|
||||
"fieldname":"party_type",
|
||||
|
@ -39,8 +39,8 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def validate_filters(filters):
|
||||
if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"):
|
||||
if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
|
||||
frappe.throw(_("{0} payment entries can not be filtered by {1}")\
|
||||
.format(filters.payment_type, filters.party_type))
|
||||
|
||||
@ -51,7 +51,7 @@ def get_columns(filters):
|
||||
_("Party Type") + "::100",
|
||||
_("Party") + ":Dynamic Link/Party Type:140",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
|
||||
_("Invoice Posting Date") + ":Date:130",
|
||||
_("Payment Due Date") + ":Date:130",
|
||||
_("Debit") + ":Currency:120",
|
||||
@ -69,7 +69,7 @@ def get_conditions(filters):
|
||||
conditions = []
|
||||
|
||||
if not filters.party_type:
|
||||
if filters.payment_type == "Outgoing":
|
||||
if filters.payment_type == _("Outgoing"):
|
||||
filters.party_type = "Supplier"
|
||||
else:
|
||||
filters.party_type = "Customer"
|
||||
@ -101,7 +101,7 @@ def get_entries(filters):
|
||||
|
||||
def get_invoice_posting_date_map(filters):
|
||||
invoice_details = {}
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
|
||||
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
|
||||
invoice_details[t.name] = t
|
||||
|
||||
|
@ -75,11 +75,11 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
||||
|
||||
datasets = []
|
||||
if income_data:
|
||||
datasets.append({'name': 'Income', 'values': income_data})
|
||||
datasets.append({'name': _('Income'), 'values': income_data})
|
||||
if expense_data:
|
||||
datasets.append({'name': 'Expense', 'values': expense_data})
|
||||
datasets.append({'name': _('Expense'), 'values': expense_data})
|
||||
if net_profit:
|
||||
datasets.append({'name': 'Net Profit/Loss', 'values': net_profit})
|
||||
datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
|
@ -605,7 +605,6 @@ erpnext.patches.v11_1.rename_depends_on_lwp
|
||||
execute:frappe.delete_doc("Report", "Inactive Items")
|
||||
erpnext.patches.v11_1.delete_scheduling_tool
|
||||
erpnext.patches.v12_0.rename_tolerance_fields
|
||||
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
|
||||
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
|
||||
erpnext.patches.v12_0.remove_patient_medical_record_page
|
||||
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
|
||||
@ -626,4 +625,5 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
|
||||
erpnext.patches.v12_0.update_ewaybill_field_position
|
||||
erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
|
||||
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
|
||||
erpnext.patches.v12_0.remove_bank_remittance_custom_fields
|
||||
erpnext.patches.v12_0.generate_leave_ledger_entries
|
@ -1,12 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from erpnext.regional.india.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "tax_category")
|
||||
frappe.reload_doc("stock", "doctype", "item_manufacturer")
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
@ -0,0 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from erpnext.regional.india.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "tax_category")
|
||||
frappe.reload_doc("stock", "doctype", "item_manufacturer")
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
if frappe.db.exists("Custom Field", "Company-bank_remittance_section"):
|
||||
deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code']
|
||||
for i in range(len(deprecated_fields)):
|
||||
frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i])
|
@ -1,190 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint,cstr, today
|
||||
from frappe import _
|
||||
import re
|
||||
import datetime
|
||||
from collections import OrderedDict
|
||||
|
||||
def create_bank_remittance_txt(name):
|
||||
payment_order = frappe.get_cached_doc("Payment Order", name)
|
||||
|
||||
no_of_records = len(payment_order.get("references"))
|
||||
total_amount = sum(entry.get("amount") for entry in payment_order.get("references"))
|
||||
|
||||
product_code, client_code, company_email = frappe.db.get_value("Company",
|
||||
filters={'name' : payment_order.company},
|
||||
fieldname=['product_code', 'client_code', 'email'])
|
||||
|
||||
header, file_name = get_header_row(payment_order, client_code)
|
||||
batch = get_batch_row(payment_order, no_of_records, total_amount, product_code)
|
||||
|
||||
detail = []
|
||||
for ref_doc in payment_order.get("references"):
|
||||
detail += get_detail_row(ref_doc, payment_order, company_email)
|
||||
|
||||
trailer = get_trailer_row(no_of_records, total_amount)
|
||||
detail_records = "\n".join(detail)
|
||||
|
||||
return "\n".join([header, batch, detail_records, trailer]), file_name
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_report(name):
|
||||
data, file_name = create_bank_remittance_txt(name)
|
||||
|
||||
f = frappe.get_doc({
|
||||
'doctype': 'File',
|
||||
'file_name': file_name,
|
||||
'content': data,
|
||||
"attached_to_doctype": 'Payment Order',
|
||||
"attached_to_name": name,
|
||||
'is_private': True
|
||||
})
|
||||
f.save()
|
||||
return {
|
||||
'file_url': f.file_url,
|
||||
'file_name': file_name
|
||||
}
|
||||
|
||||
def generate_file_name(name, company_account, date):
|
||||
''' generate file name with format (account_code)_mmdd_(payment_order_no) '''
|
||||
bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no'])
|
||||
return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt'
|
||||
|
||||
def get_header_row(doc, client_code):
|
||||
''' Returns header row and generated file name '''
|
||||
file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date)
|
||||
header = ["H"]
|
||||
header.append(validate_field_size(client_code, "Client Code", 20))
|
||||
header += [''] * 3
|
||||
header.append(validate_field_size(file_name, "File Name", 20))
|
||||
return "~".join(header), file_name
|
||||
|
||||
def get_batch_row(doc, no_of_records, total_amount, product_code):
|
||||
batch = ["B"]
|
||||
batch.append(validate_field_size(no_of_records, "No Of Records", 5))
|
||||
batch.append(validate_amount(format(total_amount, '0.2f'), 17))
|
||||
batch.append(sanitize_data(doc.name, '_')[:20])
|
||||
batch.append(format_date(doc.posting_date))
|
||||
batch.append(validate_field_size(product_code,"Product Code", 20))
|
||||
return "~".join(batch)
|
||||
|
||||
def get_detail_row(ref_doc, payment_entry, company_email):
|
||||
|
||||
payment_date = format_date(payment_entry.posting_date)
|
||||
payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry)
|
||||
supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account)
|
||||
company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no'])
|
||||
|
||||
addr_link = frappe.db.get_value('Dynamic Link',
|
||||
{
|
||||
'link_doctype': 'Supplier',
|
||||
'link_name': 'Sample Supplier',
|
||||
'parenttype':'Address',
|
||||
'parent': ('like', '%-Billing')
|
||||
}, 'parent')
|
||||
|
||||
supplier_billing_address = frappe.get_cached_doc('Address', addr_link)
|
||||
email = ','.join(filter(None, [supplier_billing_address.email_id, company_email]))
|
||||
|
||||
detail = OrderedDict(
|
||||
record_identifier='D',
|
||||
payment_ref_no=sanitize_data(ref_doc.payment_entry),
|
||||
payment_type=cstr(payment_entry.mode_of_payment)[:10],
|
||||
amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)),
|
||||
payment_date=payment_date,
|
||||
instrument_date=payment_date,
|
||||
instrument_number='',
|
||||
dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)),
|
||||
dr_description='',
|
||||
dr_ref_no='',
|
||||
cr_ref_no='',
|
||||
bank_code_indicator='M',
|
||||
beneficiary_code='',
|
||||
beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '),
|
||||
beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)),
|
||||
beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)),
|
||||
beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20),
|
||||
location='',
|
||||
print_location='',
|
||||
beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50),
|
||||
beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50),
|
||||
beneficiary_address_3='',
|
||||
beneficiary_address_4='',
|
||||
beneficiary_address_5='',
|
||||
beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20),
|
||||
beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6),
|
||||
beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20),
|
||||
beneficiary_email=cstr(email)[:255],
|
||||
beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10),
|
||||
payment_details_1='',
|
||||
payment_details_2='',
|
||||
payment_details_3='',
|
||||
payment_details_4='',
|
||||
delivery_mode=''
|
||||
)
|
||||
detail_record = ["~".join(list(detail.values()))]
|
||||
|
||||
detail_record += get_advice_rows(payment_entry)
|
||||
return detail_record
|
||||
|
||||
def get_advice_rows(payment_entry):
|
||||
''' Returns multiple advice rows for a single detail entry '''
|
||||
payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper()
|
||||
mode_of_payment = payment_entry.mode_of_payment
|
||||
advice_rows = []
|
||||
for record in payment_entry.references:
|
||||
advice = ['E']
|
||||
advice.append(cstr(mode_of_payment))
|
||||
advice.append(cstr(record.total_amount))
|
||||
advice.append('')
|
||||
advice.append(cstr(record.outstanding_amount))
|
||||
advice.append(record.reference_name)
|
||||
advice.append(format_date(record.due_date))
|
||||
advice.append(payment_entry_date)
|
||||
advice_rows.append("~".join(advice))
|
||||
return advice_rows
|
||||
|
||||
def get_trailer_row(no_of_records, total_amount):
|
||||
''' Returns trailer row '''
|
||||
trailer = ["T"]
|
||||
trailer.append(validate_field_size(no_of_records, "No of Records", 5))
|
||||
trailer.append(validate_amount(format(total_amount, "0.2f"), 17))
|
||||
return "~".join(trailer)
|
||||
|
||||
def sanitize_data(val, replace_str=''):
|
||||
''' Remove all the non-alphanumeric characters from string '''
|
||||
pattern = re.compile('[\W_]+')
|
||||
return pattern.sub(replace_str, val)
|
||||
|
||||
def format_date(val):
|
||||
''' Convert a datetime object to DD/MM/YYYY format '''
|
||||
return val.strftime("%d/%m/%Y")
|
||||
|
||||
def validate_amount(val, max_int_size):
|
||||
''' Validate amount to be within the allowed limits '''
|
||||
int_size = len(str(val).split('.')[0])
|
||||
|
||||
if int_size > max_int_size:
|
||||
frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions"))
|
||||
|
||||
return val
|
||||
|
||||
def validate_information(obj, attr, max_size):
|
||||
''' Checks if the information is not set in the system and is within the size '''
|
||||
if hasattr(obj, attr):
|
||||
return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size)
|
||||
|
||||
else:
|
||||
frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr))))
|
||||
|
||||
def validate_field_size(val, label, max_size):
|
||||
''' check the size of the val '''
|
||||
if len(cstr(val)) > max_size:
|
||||
frappe.throw(_("{0} field is limited to size {1}".format(label, max_size)))
|
||||
return cstr(val)
|
@ -407,14 +407,6 @@ def make_custom_fields(update=True):
|
||||
fieldtype='Link', options='Salary Component', insert_after='basic_component'),
|
||||
dict(fieldname='arrear_component', label='Arrear Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_component'),
|
||||
dict(fieldname='bank_remittance_section', label='Bank Remittance Settings',
|
||||
fieldtype='Section Break', collapsible=1, insert_after='arrear_component'),
|
||||
dict(fieldname='client_code', label='Client Code', fieldtype='Data',
|
||||
insert_after='bank_remittance_section'),
|
||||
dict(fieldname='remittance_column_break', fieldtype='Column Break',
|
||||
insert_after='client_code'),
|
||||
dict(fieldname='product_code', label='Product Code', fieldtype='Data',
|
||||
insert_after='remittance_column_break'),
|
||||
],
|
||||
'Employee Tax Exemption Declaration':[
|
||||
dict(fieldname='hra_section', label='HRA Exemption',
|
||||
|
@ -4,7 +4,7 @@ frappe.provide('erpnext.pos');
|
||||
frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: 'Point of Sale',
|
||||
title: __('Point of Sale'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
|
@ -124,6 +124,7 @@ class Item(WebsiteGenerator):
|
||||
self.update_defaults_from_item_group()
|
||||
self.validate_auto_reorder_enabled_in_stock_settings()
|
||||
self.cant_change()
|
||||
self.update_show_in_website()
|
||||
|
||||
if not self.get("__islocal"):
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
@ -476,6 +477,10 @@ class Item(WebsiteGenerator):
|
||||
|
||||
[self.remove(d) for d in to_remove]
|
||||
|
||||
def update_show_in_website(self):
|
||||
if self.disabled:
|
||||
self.show_in_website = False
|
||||
|
||||
def update_template_tables(self):
|
||||
template = frappe.get_doc("Item", self.variant_of)
|
||||
|
||||
|
@ -1 +1,3 @@
|
||||
<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext</a>
|
||||
{% set domains = frappe.get_doc("Domain Settings").active_domains %}
|
||||
|
||||
<a href="https://erpnext.com?source=website_footer" target="_blank" class="text-muted">Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software</a>
|
||||
|
Loading…
Reference in New Issue
Block a user