forked from Mapan/odoo17e
170 lines
6.5 KiB
Python
170 lines
6.5 KiB
Python
# coding: utf-8
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import base64
|
|
import csv
|
|
import io
|
|
|
|
from odoo import api, models, fields
|
|
|
|
|
|
class Generate1099Wizard(models.TransientModel):
|
|
_name = "l10n_us_1099.wizard"
|
|
_description = "Exports 1099 data to a CSV file."
|
|
|
|
def _default_start_date(self):
|
|
""" Return the first day of last year. """
|
|
today = fields.Date.today()
|
|
return today.replace(today.year - 1, 1, 1)
|
|
|
|
def _default_end_date(self):
|
|
""" Return the last day of last year. """
|
|
today = fields.Date.today()
|
|
return today.replace(today.year - 1, 12, 31)
|
|
|
|
start_date = fields.Date(
|
|
"Start Date",
|
|
default=_default_start_date,
|
|
required=True,
|
|
help="The wizard will auto-populate journal items on and after this date."
|
|
)
|
|
end_date = fields.Date(
|
|
"End Date", default=_default_end_date,
|
|
required=True,
|
|
help="The wizard will auto-populate journal items before and on this date."
|
|
)
|
|
lines_to_export = fields.Many2many(
|
|
"account.move.line",
|
|
string="Journal Items To Include",
|
|
compute="_compute_lines_to_export",
|
|
readonly=False,
|
|
store=True,
|
|
help="These journal items will be included in the generated CSV file."
|
|
)
|
|
generated_csv_file = fields.Binary(
|
|
"Generated file",
|
|
help="Technical field used to temporarily hold the generated CSV file before it's downloaded."
|
|
)
|
|
|
|
@api.depends("start_date", "end_date")
|
|
def _compute_lines_to_export(self):
|
|
""" This adds lines that moved money out of an asset (e.g. a bank account) to a vendor that requires a 1099.
|
|
The IRS wants only money payments, so we cannot use journal items on other accounts (e.g. Rent or Expense)
|
|
since they can be paid in different ways (e.g. a credit somewhere else). It also should show refunds for vendor bills,
|
|
since they are allowed to use to offset the reported amount."""
|
|
for wizard in self:
|
|
wizard.lines_to_export = self.env["account.move.line"]
|
|
lines = self.env["account.move.line"].search([
|
|
("company_id", "in", self.env.companies.ids),
|
|
("parent_state", "=", "posted"),
|
|
("currency_id", "=", self.env.ref("base.USD").id),
|
|
("partner_id.box_1099_id", "!=", False),
|
|
("date", ">=", wizard.start_date),
|
|
("date", "<=", wizard.end_date),
|
|
# everything in accounts under Balance Sheet > Assets that's liquid
|
|
("account_id.internal_group", "=", "asset"),
|
|
("account_id.account_type", "in", ("asset_cash", "liability_credit_card")),
|
|
], order="partner_id,date")
|
|
|
|
# only allow positive lines if they're related to a vendor bill refund
|
|
for line in lines:
|
|
if line.balance > 0:
|
|
reconciled_lines = line.move_id.line_ids._reconciled_lines()
|
|
types = self.env['account.move.line'].browse(reconciled_lines).mapped('move_id').mapped('move_type')
|
|
if "in_refund" in types:
|
|
wizard.lines_to_export |= line
|
|
else:
|
|
wizard.lines_to_export |= line
|
|
|
|
def _generate_row(self, company, vendor, total, boxes_1099):
|
|
""" Generates a single row in the output CSV. Will attribute the total to the box specified on the partner. """
|
|
row = [
|
|
company.display_name,
|
|
company.street,
|
|
company.street2,
|
|
company.city,
|
|
company.state_id.name,
|
|
company.zip,
|
|
company.country_id.name,
|
|
company.phone,
|
|
company.vat,
|
|
vendor.display_name,
|
|
vendor.street,
|
|
vendor.street2,
|
|
vendor.city,
|
|
vendor.state_id.name,
|
|
vendor.zip,
|
|
vendor.country_id.name,
|
|
vendor.email,
|
|
vendor.vat,
|
|
]
|
|
row = [val or "" for val in row] # replace False m2o's
|
|
|
|
for box_1099 in boxes_1099:
|
|
if box_1099 == vendor.box_1099_id:
|
|
row.append(-total) # payments to vendors will be negative, so flip the sign
|
|
else:
|
|
row.append(0)
|
|
|
|
return row
|
|
|
|
def action_generate(self):
|
|
""" Called from UI. Generates the CSV file in memory and writes it to the generated_csv_file
|
|
field. Then returns an action for the client to download it. """
|
|
self.ensure_one()
|
|
header = [
|
|
"Payer Name",
|
|
"Payer Address Line 1",
|
|
"Payer Address Line 2",
|
|
"Payer City",
|
|
"Payer State",
|
|
"Payer Zip",
|
|
"Payer Country",
|
|
"Payer Phone Number",
|
|
"Payer TIN",
|
|
"Payee Name",
|
|
"Payee Address Line 1",
|
|
"Payee Address Line 2",
|
|
"Payee City",
|
|
"Payee State",
|
|
"Payee Zip",
|
|
"Payee Country",
|
|
"Payee Email",
|
|
"Payee TIN",
|
|
]
|
|
boxes_1099 = self.env["l10n_us.1099_box"].search([])
|
|
header += boxes_1099.mapped("name")
|
|
|
|
output = io.StringIO()
|
|
writer = csv.writer(output)
|
|
writer.writerow(header)
|
|
|
|
curr_vendor = None
|
|
curr_total = 0
|
|
lines = self.lines_to_export.sorted(lambda l: l.partner_id.id)
|
|
for line in lines:
|
|
if curr_vendor != line.partner_id and curr_total != 0:
|
|
curr_total = self.env.ref("base.USD").round(curr_total)
|
|
new_row = self._generate_row(line.company_id, curr_vendor, curr_total, boxes_1099)
|
|
writer.writerow(new_row)
|
|
|
|
curr_vendor = line.partner_id
|
|
curr_total = line.balance
|
|
else:
|
|
curr_vendor = line.partner_id
|
|
curr_total += line.balance
|
|
|
|
if curr_total != 0:
|
|
curr_total = self.env.ref("base.USD").round(curr_total)
|
|
writer.writerow(self._generate_row(lines[-1].company_id, curr_vendor, curr_total, boxes_1099))
|
|
|
|
self.generated_csv_file = base64.b64encode(output.getvalue().encode())
|
|
|
|
us_format = "%m_%d_%Y"
|
|
return {
|
|
"type": "ir.actions.act_url",
|
|
"target": "self",
|
|
"url": "/web/content?model=l10n_us_1099.wizard&download=true&field=generated_csv_file&filename=1099 report {} - {}.csv&id={}".format(
|
|
self.start_date.strftime(us_format), self.end_date.strftime(us_format), self.id
|
|
),
|
|
}
|