Create a new modules and update the invoice module

Good morning, i have an preoccupation in a development mode.
I need to create an another module that modify the invoice module.

For example i need to create a discount module that will modify the total amount in the invoice.

it’s possible to add the modification of the total amount directly in the invoice.py file of account_invoice module, but not needed for me.

for more specific example, i need to use and modify this function in invoice.py file for my discount module

def _get_re_line(self, date, amount):
        '''
        Return move line
        '''
        pool = Pool()
        User = Pool().get('company.company')
        user = User(Transaction().user)
        Currency = pool.get('currency.currency')
        MoveLine = pool.get('account.move.line')
        line = MoveLine()
        if self.currency != self.company.currency:
            with Transaction().set_context(date=self.currency_date):
                line.amount_second_currency = Currency.compute(
                    self.company.currency, amount, self.currency)
            line.second_currency = self.currency
        else:
            line.amount_second_currency = None
            line.second_currency = None
        if amount <= 0:
            line.debit, line.credit = -amount, 0
        else:
            line.debit, line.credit = 0, amount
        if line.amount_second_currency:
            line.amount_second_currency = (
                line.amount_second_currency.copy_sign(
                    line.debit - line.credit))
        line.account = self.health_service.company.party.account_receivable
        if self.account.party_required:
            line.party = self.health_service.company.party
        line.maturity_date = date
        line.description = self.description
        return line

but this function exist in the invoice.py file of account_invoice module.

Is it advisable to proceed in this way? And how can I be sure that it’s my module that’s being used and not the invoice.py module?

If possible, could I have a link to the documentation that explains my case?

You should never modify the modules of tryton otherwise they modifications will be lost on updates.

In order to support that, you need to create a module that extends the original module. For example if order to extend the above mentioned function you will need to do a new class which does something like:

def _get_re_line(self, date, amount):
    line = super().get_re_line(date, amount)
    # Do your modifications on the line
    return line

This way the modifications will be only executed when your module is activated and the original code does not need to be modified.
This is explained in the module tutorial:

https://docs.tryton.org/latest/server/tutorial/module/extend.html

On the tutorial only new fields are explained but you can modify any attribute of the parent module, including functions and existing fields definition. You have an example in how to extend a file in the folllowing MR:

Here you can do only one thing, return line an Line an do the modification.

But my objective is to modify the interior of the function before the return line

You do not need to modify the inner function but modify the returned object so the values saved to the database are not the ones of the inner function but the ones you set on your custom code.

Could you explain what are you trying to achieve? That will help us to provide better advices!

I want to be able to modify the total amount of a bill according to a percentage present in the health service.

To do this, I want to create another module that will make this modification so that when I generate the invoice from the service, the amount is automatically calculated and appears on the draft invoice.

Note that in this case, the next function used to generate the invoice is a wizard and returns “end”.

def transition_create_service_invoice(self):
        print('creation de la facture')
        pool = Pool()
        HealthService = pool.get('gnuhealth.health_service')
        Invoice = pool.get('account.invoice')
        Party = pool.get('party.party')
        Journal = pool.get('account.journal')
        AcctConfig = pool.get('account.configuration')
        acct_config = AcctConfig(1)

        currency_id = Transaction().context.get('currency')

        services = HealthService.browse(Transaction().context.get(
            'active_ids'))
        invoices = []

        # Invoice Header
        for service in services:
            if service.state == 'invoiced':
                raise ServiceInvoiced(
                    gettext('health_insurance.msg_service_invoiced')
                    )
            if service.invoice_to:
                party = service.invoice_to
            else:
                party = service.patient.name
            invoice_data = {}
            invoice_data['description'] = service.desc
            invoice_data['party'] = party.id
            invoice_data['type'] = 'out'
            invoice_data['invoice_date'] = datetime.date.today()
            invoice_data['company'] = service.company.id

            """ Look for the AR account in the following order:
                * Party
                * Default AR in accounting config
                * Raise an error if there is no AR account
            """
            if (party.account_receivable):
                invoice_data['account'] = party.account_receivable.id
            elif (acct_config.default_account_receivable):
                invoice_data['account'] = \
                    acct_config.default_account_receivable.id
            else:
                raise NoAccountReceivable(
                    gettext('health_insurance.msg_no_account_receivable'))

            ctx = {}
            sale_price_list = None
            if hasattr(party, 'sale_price_list'):
                sale_price_list = party.sale_price_list

            if sale_price_list:
                ctx['price_list'] = sale_price_list.id
                ctx['sale_date'] = datetime.date.today()
                ctx['currency'] = currency_id
                ctx['customer'] = party.id

            journals = Journal.search([
                ('type', '=', 'revenue'),
                ], limit=1)

            if journals:
                journal, = journals
            else:
                journal = None

            invoice_data['journal'] = journal.id

            party_address = Party.address_get(party, type='invoice')
            if not party_address:
                raise NoInvoiceAddress(
                    gettext('health_insurance.msg_no_invoice_address')
                    )

            invoice_data['invoice_address'] = party_address.id
            invoice_data['reference'] = service.name

            """ Look for the payment term in the following order:
                * Party
                * Default payment term in accounting config
                * Raise an error if there is no payment term
            """
            if (party.customer_payment_term):
                invoice_data['payment_term'] = party.customer_payment_term.id
            elif (acct_config.default_customer_payment_term):
                invoice_data['payment_term'] = \
                    acct_config.default_customer_payment_term.id
            else:
                raise NoPaymentTerm(
                    gettext('health_insurance.msg_no_payment_term')
                    )

            # Invoice Lines
            seq = 0
            invoice_lines = []
            for line in service.service_line:
                seq = seq + 1
                account = line.product.template.account_revenue_used.id

                if sale_price_list:
                    with Transaction().set_context(ctx):
                        unit_price = sale_price_list.compute(
                            party,
                            line.product, line.product.list_price,
                            line.qty, line.product.default_uom)
                        unit_price2 = sale_price_list.compute(
                            party,
                            line.product, line.product.list_price,
                            line.qty, line.product.default_uom)
                else:
                    unit_price = line.product.list_price
                    unit_price2 = line.product.list_price

                if line.to_invoice:
                    taxes = []
                    desc = line.desc

                    # Include taxes related to the product on the invoice line
                    for product_tax_line in line.product.customer_taxes_used:
                        taxes.append(product_tax_line.id)

                    # Check the Insurance policy for this service
                    if service.insurance_plan:
                        discount = self.discount_policy(
                            service.insurance_plan,
                            line.product)
                        if service.insurance_plan.part_as:
                            assurance = service.insurance_plan.part_as
                            invoice_data['part_ass'] = assurance

                        if discount:
                            if 'value' in list(discount.keys()):
                                if discount['value']:
                                    if (discount['type'] == 'pct'):
                                        unit_price *= decimal.Decimal(
                                            1 - discount['value']/100)
                                        # Use price_decimal value from
                                        # system configuration to set
                                        # the number of decimals
                                        unit_price = round_price(unit_price)
                                        unit_price2 = unit_price*decimal.Decimal(discount['value'] / 100)
                                        unit_price2 = round_price(unit_price2)
                                        print('prix2', unit_price2)

                                        # Add remark on description discount
                                        str_disc = str(discount['value']) + '%'
                                        desc = line.desc + " (Assurance " + \
                                               str(str_disc) + ")"

                    invoice_lines.append(('create', [{
                            'origin': str(line),
                            'product': line.product.id,
                            'description': desc,
                            'quantity': line.qty,
                            'account': account,
                            'unit': line.product.default_uom.id,
                            'unit_price': unit_price,
                            'unit_price2': unit_price2,
                            'sequence': seq,
                            'taxes': [('add', taxes)],
                        }]))
                invoice_data['lines'] = invoice_lines


            invoices.append(invoice_data)

        Invoice.update_taxes(Invoice.create(invoices))

        # Change to invoiced the status on the service document.
        HealthService.write(services, {'state': 'invoiced'})

        return 'end'

You must change the unit price of each line by this percentage.
You can not change the move line amount otherwise the move will no more have a balance of 0 or the move will store different revenue than the amount printed on the invoice.