Merge pull request #16421 from agritheory/usregional
feat: US Regional Module with IRS 1099 reporting
This commit is contained in:
commit
94d6f94e0e
File diff suppressed because one or more lines are too long
0
erpnext/regional/report/irs_1099/__init__.py
Normal file
0
erpnext/regional/report/irs_1099/__init__.py
Normal file
47
erpnext/regional/report/irs_1099/irs_1099.js
Normal file
47
erpnext/regional/report/irs_1099/irs_1099.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
frappe.query_reports["IRS 1099"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
|
"reqd": 1,
|
||||||
|
"width": 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"fiscal_year",
|
||||||
|
"label": __("Fiscal Year"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"reqd": 1,
|
||||||
|
"width": 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"supplier_group",
|
||||||
|
"label": __("Supplier Group"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier Group",
|
||||||
|
"default": "",
|
||||||
|
"reqd": 0,
|
||||||
|
"width": 80
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
onload: function(query_report) {
|
||||||
|
query_report.page.add_inner_button(__("Print IRS 1099 Forms"), () => {
|
||||||
|
build_1099_print(query_report);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function build_1099_print(query_report){
|
||||||
|
let filters = JSON.stringify(query_report.get_values());
|
||||||
|
let w = window.open('/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?' +
|
||||||
|
'&filters=' + encodeURIComponent(filters));
|
||||||
|
// w.print();
|
||||||
|
}
|
29
erpnext/regional/report/irs_1099/irs_1099.json
Normal file
29
erpnext/regional/report/irs_1099/irs_1099.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"color": "#c0392b",
|
||||||
|
"creation": "2018-09-10 00:00:00",
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"icon": "fa fa-star",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"letter_head": "",
|
||||||
|
"modified": "2019-01-17 08:30:41.863572",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "IRS 1099",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Supplier",
|
||||||
|
"report_name": "IRS 1099",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "System Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
151
erpnext/regional/report/irs_1099/irs_1099.py
Normal file
151
erpnext/regional/report/irs_1099/irs_1099.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
import json
|
||||||
|
from frappe import _, _dict
|
||||||
|
from frappe.utils import nowdate
|
||||||
|
from frappe.utils.data import fmt_money
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
from PyPDF2 import PdfFileWriter
|
||||||
|
from frappe.utils.pdf import get_pdf
|
||||||
|
from frappe.utils.print_format import read_multi_pdf
|
||||||
|
from frappe.utils.jinja import render_template
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
filters = filters if isinstance(filters, _dict) else _dict(filters)
|
||||||
|
if not filters:
|
||||||
|
filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0])
|
||||||
|
filters.setdefault('company', frappe.db.get_default("company"))
|
||||||
|
data = []
|
||||||
|
columns = get_columns()
|
||||||
|
data = frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
s.supplier_group as "supplier_group",
|
||||||
|
gl.party AS "supplier",
|
||||||
|
s.tax_id as "tax_id",
|
||||||
|
SUM(gl.debit) AS "payments"
|
||||||
|
FROM
|
||||||
|
`tabGL Entry` gl INNER JOIN `tabSupplier` s
|
||||||
|
WHERE
|
||||||
|
s.name = gl.party
|
||||||
|
AND s.irs_1099 = 1
|
||||||
|
AND gl.fiscal_year = %(fiscal_year)s
|
||||||
|
AND gl.party_type = "Supplier"
|
||||||
|
|
||||||
|
GROUP BY
|
||||||
|
gl.party
|
||||||
|
|
||||||
|
ORDER BY
|
||||||
|
gl.party DESC""", {"fiscal_year": filters.fiscal_year,
|
||||||
|
"supplier_group": filters.supplier_group,
|
||||||
|
"company": filters.company}, as_dict=True)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"fieldname": "supplier_group",
|
||||||
|
"label": _("Supplier Group"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier Group",
|
||||||
|
"width": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"label": _("Supplier"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier",
|
||||||
|
"width": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax_id",
|
||||||
|
"label": _("Tax ID"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
"fieldname": "payments",
|
||||||
|
"label": _("Total Payments"),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def irs_1099_print(filters):
|
||||||
|
if not filters:
|
||||||
|
frappe._dict({
|
||||||
|
"company": frappe.db.get_default("Company"),
|
||||||
|
"fiscal_year": frappe.db.get_default("fiscal_year")})
|
||||||
|
else:
|
||||||
|
filters = frappe._dict(json.loads(filters))
|
||||||
|
company_address = get_payer_address_html(filters.company)
|
||||||
|
company_tin = frappe.db.get_value("Company", filters.company, "tax_id")
|
||||||
|
columns, data = execute(filters)
|
||||||
|
template = frappe.get_doc("Print Format", "IRS 1099 Form").html
|
||||||
|
output = PdfFileWriter()
|
||||||
|
for row in data:
|
||||||
|
row["company"] = filters.company
|
||||||
|
row["company_tin"] = company_tin
|
||||||
|
row["payer_street_address"] = company_address
|
||||||
|
row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html("Supplier", row.supplier)
|
||||||
|
row["payments"] = fmt_money(row["payments"], precision=0, currency="USD")
|
||||||
|
frappe._dict(row)
|
||||||
|
print(row)
|
||||||
|
pdf = get_pdf(render_template(template, row), output=output if output else None)
|
||||||
|
print(pdf)
|
||||||
|
frappe.local.response.filename = filters.fiscal_year + " " + filters.company + " IRS 1099 Forms"
|
||||||
|
frappe.local.response.filecontent = read_multi_pdf(output)
|
||||||
|
frappe.local.response.type = "download"
|
||||||
|
|
||||||
|
|
||||||
|
def get_payer_address_html(company):
|
||||||
|
address_list = frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
tabAddress
|
||||||
|
WHERE
|
||||||
|
is_your_company_address = 1
|
||||||
|
ORDER BY
|
||||||
|
address_type="Postal" DESC, address_type="Billing" DESC
|
||||||
|
LIMIT 1
|
||||||
|
""", {"company": company}, as_dict=True)
|
||||||
|
if address_list:
|
||||||
|
company_address = address_list[0]["name"]
|
||||||
|
return frappe.get_doc("Address", company_address).get_display()
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_street_address_html(party_type, party):
|
||||||
|
address_list = frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
link.parent
|
||||||
|
FROM `tabDynamic Link` link, `tabAddress` address
|
||||||
|
WHERE link.parenttype = "Address"
|
||||||
|
AND link.link_name = %(party)s
|
||||||
|
ORDER BY address.address_type="Postal" DESC,
|
||||||
|
address.address_type="Billing" DESC
|
||||||
|
LIMIT 1
|
||||||
|
""", {"party": party}, as_dict=True)
|
||||||
|
if address_list:
|
||||||
|
supplier_address = address_list[0]["parent"]
|
||||||
|
doc = frappe.get_doc("Address", supplier_address)
|
||||||
|
if doc.address_line2:
|
||||||
|
street = doc.address_line1 + "<br>\n" + doc.address_line2 + "<br>\n"
|
||||||
|
else:
|
||||||
|
street = doc.address_line1 + "<br>\n"
|
||||||
|
city = doc.city + ", " if doc.city else ""
|
||||||
|
city = city + doc.state + " " if doc.state else city
|
||||||
|
city = city + doc.pincode if doc.pincode else city
|
||||||
|
city += "<br>\n"
|
||||||
|
return street, city
|
||||||
|
else:
|
||||||
|
return "", ""
|
0
erpnext/regional/united_states/__init__.py
Normal file
0
erpnext/regional/united_states/__init__.py
Normal file
4
erpnext/regional/united_states/address_template.html
Normal file
4
erpnext/regional/united_states/address_template.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{{ address_line1 }}<br>
|
||||||
|
{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}
|
||||||
|
{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}<br>{% endif -%}
|
||||||
|
{% if country != "United States" %}{{ country|upper }}{% endif -%}
|
42
erpnext/regional/united_states/setup.py
Normal file
42
erpnext/regional/united_states/setup.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
|
|
||||||
|
|
||||||
|
def setup(company=None, patch=True):
|
||||||
|
make_custom_fields()
|
||||||
|
add_print_formats()
|
||||||
|
update_address_template()
|
||||||
|
|
||||||
|
|
||||||
|
def make_custom_fields():
|
||||||
|
custom_fields = {
|
||||||
|
'Supplier': [
|
||||||
|
dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id',
|
||||||
|
label='Is IRS 1099 reporting required for supplier?')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
create_custom_fields(custom_fields)
|
||||||
|
|
||||||
|
|
||||||
|
def add_print_formats():
|
||||||
|
frappe.reload_doc("regional", "print_format", "irs_1099_form")
|
||||||
|
frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where
|
||||||
|
name in('IRS 1099 Form') """)
|
||||||
|
|
||||||
|
|
||||||
|
def update_address_template():
|
||||||
|
html = """{{ address_line1 }}<br>
|
||||||
|
{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}
|
||||||
|
{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}<br>{% endif -%}
|
||||||
|
{% if country != "United States" %}{{ country|upper }}{% endif -%}
|
||||||
|
"""
|
||||||
|
|
||||||
|
address_template = frappe.db.get_value('Address Template', 'United States')
|
||||||
|
if address_template:
|
||||||
|
frappe.db.set_value('Address Template', 'United States', 'template', html)
|
||||||
|
else:
|
||||||
|
frappe.get_doc(dict(doctype='Address Template', country='United States', template=html)).insert()
|
48
erpnext/regional/united_states/test_united_states.py
Normal file
48
erpnext/regional/united_states/test_united_states.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
import unittest
|
||||||
|
from erpnext.regional.report.irs_1099.irs_1099 import execute as execute_1099_report
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnitedStates(unittest.TestCase):
|
||||||
|
def test_irs_1099_custom_field(self):
|
||||||
|
doc = frappe.new_doc("Supplier")
|
||||||
|
doc.supplier_name = "_US 1099 Test Supplier"
|
||||||
|
doc.supplier_group = "Services"
|
||||||
|
doc.supplier_type = "Company"
|
||||||
|
doc.country = "United States"
|
||||||
|
doc.tax_id = "04-1234567"
|
||||||
|
doc.irs_1099 = 1
|
||||||
|
doc.save()
|
||||||
|
frappe.db.commit()
|
||||||
|
supplier = frappe.get_doc('Supplier', "_US 1099 Test Supplier")
|
||||||
|
self.assertEqual(supplier.irs_1099, 1)
|
||||||
|
|
||||||
|
def test_irs_1099_report(self):
|
||||||
|
make_payment_entry_to_irs_1099_supplier()
|
||||||
|
filters = frappe._dict({"fiscal_year": "2016", "company": "_Test Company"})
|
||||||
|
columns, data = execute_1099_report(filters)
|
||||||
|
print(columns, data)
|
||||||
|
expected_row = {'supplier': '_US 1099 Test Supplier',
|
||||||
|
'supplier_group': 'Services',
|
||||||
|
'payments': 100.0,
|
||||||
|
'tax_id': '04-1234567'}
|
||||||
|
self.assertEqual(data, expected_row)
|
||||||
|
|
||||||
|
|
||||||
|
def make_payment_entry_to_irs_1099_supplier():
|
||||||
|
pe = frappe.new_doc("Payment Entry")
|
||||||
|
pe.payment_type = "Pay"
|
||||||
|
pe.company = "_Test Company"
|
||||||
|
pe.posting_date = "2016-01-10"
|
||||||
|
pe.paid_from = "_Test Bank USD - _TC"
|
||||||
|
pe.paid_to = "_Test Bank - _TC"
|
||||||
|
pe.paid_amount = 100
|
||||||
|
pe.received_amount = 100
|
||||||
|
pe.reference_no = "For IRS 1099 testing"
|
||||||
|
pe.reference_date = "2016-01-10"
|
||||||
|
pe.party_type = "Supplier"
|
||||||
|
pe.party = "_US 1099 Test Supplier"
|
||||||
|
pe.save()
|
Loading…
x
Reference in New Issue
Block a user