diff --git a/yuthon_property/models/property_accounts_receivable.py b/yuthon_property/models/property_accounts_receivable.py index fa978fb60..df45bc916 100644 --- a/yuthon_property/models/property_accounts_receivable.py +++ b/yuthon_property/models/property_accounts_receivable.py @@ -3,6 +3,8 @@ import xlwt import base64 import io from openpyxl import load_workbook +from openpyxl.styles import Alignment +from copy import copy from odoo.modules import get_module_resource from datetime import datetime, timedelta, date @@ -286,3 +288,128 @@ class PropertyAccountsReceivable(models.Model): 'url': f'/web/content/{attachment.id}?download=true', 'target': 'self', } + + def export_kingdee_receivable(self): + """导出应收单金蝶数据到Excel模板""" + # 获取勾选的应收账单记录 + receivable_ids = self._context.get('active_ids', []) + if not receivable_ids: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'message': '请先勾选需要导出的应收账单记录', + 'type': 'warning', + 'sticky': False, + } + } + + receivable_records = self.env['property.accounts.receivable'].browse(receivable_ids) + + # 加载模板文件 + template_path = get_module_resource('yuthon_property', 'static', 'tmpl', 'yingshoudanjindiedaoru.xlsx') + workbook = load_workbook(template_path) + sheet = workbook.active + + # 从第3行开始写入数据(第1-2行是表头) + row = 3 + # 序号起始值 + sequence_number = 104101 + + # 获取模板样式行(使用第3行作为参考,但会被数据覆盖) + # 建议:确保模板第3行有完整的样式设置 + template_row_idx = 3 + if sheet.max_row >= template_row_idx: + # 先保存第3行的样式 + template_styles = {} + for col in range(1, 37): # A到AJ列(36列) + cell = sheet.cell(row=template_row_idx, column=col) + template_styles[col] = { + 'font': copy(cell.font), + 'fill': copy(cell.fill), + 'border': copy(cell.border), + 'alignment': copy(cell.alignment), + } + else: + template_styles = {} + + for record in receivable_records: + # 先解除当前行的合并单元格(如果有) + merged_cells_to_unmerge = [] + for merged_range in sheet.merged_cells.ranges: + if merged_range.min_row <= row <= merged_range.max_row: + merged_cells_to_unmerge.append(str(merged_range)) + for merged_range_str in merged_cells_to_unmerge: + sheet.unmerge_cells(merged_range_str) + + # 先为所有列应用模板样式 + for col in range(1, 37): # A到AJ列 + cell = sheet.cell(row=row, column=col) + if col in template_styles: + cell.font = copy(template_styles[col]['font']) + cell.fill = copy(template_styles[col]['fill']) + cell.border = copy(template_styles[col]['border']) + cell.alignment = copy(template_styles[col]['alignment']) + + # A列:序号(从104101开始) + sheet.cell(row=row, column=1, value=sequence_number) + + # B列:应收开始日期 + if record.due_date: + sheet.cell(row=row, column=2, value=record.due_date.strftime('%Y-%m-%d')) + + # C列:应收结束日期 + if record.due_date_end: + sheet.cell(row=row, column=3, value=record.due_date_end.strftime('%Y-%m-%d')) + + # E列:物业地址(设置自动换行) + e_cell = sheet.cell(row=row, column=5, value=record.property_name or '') + e_cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + + # M列:租赁单位(第13列) + rent_company = '' + if record.contract_id: + rent_company = record.contract_id.rent_company or '' + sheet.cell(row=row, column=13, value=rent_company) + + # N列:备注(设置自动换行) + n_cell = sheet.cell(row=row, column=14, value=record.remark or '') + n_cell.alignment = Alignment(horizontal='left', vertical='center', wrap_text=True) + + # 设置行高为自动(让Excel自动调整) + # 为确保自动换行生效,设置最小行高 + if not sheet.row_dimensions[row].height or sheet.row_dimensions[row].height < 30: + sheet.row_dimensions[row].height = 30 # 设置最小行高30,确保能显示2-3行文本 + + # P列:承租方 + sheet.cell(row=row, column=16, value=record.lessee_id.name if record.lessee_id else '') + + # AF列:税率(第32列) + sheet.cell(row=row, column=32, value=record.tax or 0) + + # AJ列:当期金额(第36列) + sheet.cell(row=row, column=36, value=record.original_amount or 0) + + row += 1 + sequence_number += 1 + + # 保存到内存 + output = io.BytesIO() + workbook.save(output) + output.seek(0) + excel_data = base64.b64encode(output.read()) + output.close() + + # 创建附件并返回下载 + attachment = self.env['ir.attachment'].create({ + 'name': '应收单金蝶导入.xlsx', + 'datas': excel_data, + 'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'type': 'binary', + }) + + return { + 'type': 'ir.actions.act_url', + 'url': f'/web/content/{attachment.id}?download=true', + 'target': 'self', + } diff --git a/yuthon_property/static/tmpl/yingshoudanjindiedaoru.xlsx b/yuthon_property/static/tmpl/yingshoudanjindiedaoru.xlsx new file mode 100644 index 000000000..55fd2713b Binary files /dev/null and b/yuthon_property/static/tmpl/yingshoudanjindiedaoru.xlsx differ diff --git a/yuthon_property/views/property_accounts_receivable_views.xml b/yuthon_property/views/property_accounts_receivable_views.xml index c0b48def5..2c05e23b6 100644 --- a/yuthon_property/views/property_accounts_receivable_views.xml +++ b/yuthon_property/views/property_accounts_receivable_views.xml @@ -162,4 +162,12 @@ code action = model.export_batch_invoice() + + 应收单金蝶导出 + + + list + code + action = model.export_kingdee_receivable() + \ No newline at end of file diff --git a/yuthon_salary/views/yuthon_salary_active_views.xml b/yuthon_salary/views/yuthon_salary_active_views.xml index a38353af0..0b3ae8375 100644 --- a/yuthon_salary/views/yuthon_salary_active_views.xml +++ b/yuthon_salary/views/yuthon_salary_active_views.xml @@ -36,9 +36,6 @@ - - - @@ -71,6 +68,9 @@ + + +