Search code examples
pythoncsverror-handlingexportodoo-16

generate csv export downloadable odoo


i have a problem to generate downloadable export to csv file, does anyone know the problem? this is my transient model


class ReportPatientWizard(models.TransientModel):
    _name = "report.patient.wizard"
    _description = "Patient Reports"

    patient_id = fields.Many2one('hospital.patient', string='Patient', readonly=True)
    gender_filter = fields.Selection([('male', 'Male'), ('female', 'Female'), ('other', 'Other')],
                                     string='Gender Filter')

    def _prepare_csv_data(self, patients):
        field_names = ['Name', 'Age', 'Gender']  # Field names for CSV headers
        data = io.StringIO()
        writer = csv.DictWriter(data, fieldnames=field_names)

        writer.writeheader()  # Write CSV header

        for patient in patients:
            row = {
                'Name': patient.name,
                'Age': patient.age,
                'Gender': patient.gender,
                # Add other fields as needed
            }
            writer.writerow(row)

        return data.getvalue()

    @api.multi
    def download_patient_report(self):
        # Ambil data pasien berdasarkan ID yang dipilih
        patient = self.patient_id

        # Filter data berdasarkan gender jika gender_filter dipilih
        if self.gender_filter:
            patients = self.env['hospital.patient'].search([('gender', '=', self.gender_filter)])
        else:
            patients = self.env['hospital.patient'].search([])

        if not patients:
            raise UserError(_('No patients found matching the criteria.'))

        csv_data = self._prepare_csv_data(patients)

        print(f"Records: {patients}")

        # Prepare file name
        filename = f'patient_report_{fields.Date.today()}.csv'

        # Return a response to download the CSV file
        return {
            'type': 'ir.actions.act_url',
            'url': 'web/content/?model=report.patient.wizard&id={}&filename={}&field=file'.format(self.id, filename),
            'target': 'current',
        }

    def print_patient_report(self):
        # Generate and download patient report as CSV
        return self.download_patient_report()

and this is the view.xml

<record id="report_patient_view" model="ir.ui.view">
        <field name="name">Report Patient</field>
         <field name="model">report.patient.wizard</field>
        <field name="arch" type="xml">
            <form string="Report Patient">
<!--                <field name="patient_id"/>-->
                <group col="1">
<!--                    <field name="is_gender"/>-->
                    <field name="gender_filter"/>
                </group>
<!--                <field name="journal_ids" required="0" invisible="1"/>-->
                <footer>
                    <button name="download_patient_report" string="Download" type="object" class="oe_highlight"/>
                    <button string="Cancel" class="btn btn-default" special="cancel"/>
                </footer>
            </form>
        </field>
    </record>

    <record id="action_report_patient_view" model="ir.actions.act_window">
        <field name="name">Report Patient</field>
        <field name="res_model">report.patient.wizard</field>
        <field name="type">ir.actions.act_window</field>
        <field name="view_mode">form</field>
        <field name="view_id" ref="report_patient_view"/>
        <field name="target">new</field>
    </record>

    <menuitem id="menu_report"
              name="Report Patients"
              parent="menu_hospital_operations"
              action="action_report_patient_view"
              sequence="15"/>

in here i want to make a form filter that the user can select which gender they want to take the data, but after i clicked the download button it shows the problem this is the error

what i was expecting is when i clicked the download button it will generate the download


Solution

  • You specified a field in the route and you don't have a field named file. You need to add a binary field and set its content just after the file name

    Example:

    # Prepare file name
    filename = f'patient_report_{fields.Date.today()}.csv'
    self.file = base64.b64encode(csv_data.encode('utf-8'))
    

    You can add self.ensure_one() at the beginning of the download_patient_report function to make sure that self holds a single record