示例#1
0
 def _cron_cleanup_obsolete(self, days=7):
     from_date = fields.Datetime.now().replace(hour=23,
                                               minute=59,
                                               second=59)
     limit_date = date_utils.subtract(from_date, days)
     records = self.search([("state", "=", "done"),
                            ("done_on", "<=", limit_date)])
     records.unlink()
     _logger.info("Cleanup obsolete images import. %d records found.",
                  len(records))
示例#2
0
 def convertString2Datetime(self, strFull):
     """
     Convert string to datetime
     :param strFull: EX: 2019-07-04T20:00:00.000-0400
     :return: datetime (with timezone is UTC)
     """
     strDateTime = strFull[:strFull.find(".")].replace("T", " ")
     dateTime = fields.Datetime.to_datetime(strDateTime)
     strTZ = strFull[-4:]
     if strFull.find("+") == -1:
         dateTime = date_utils.add(dateTime,
                                   hours=int(strTZ[:2]),
                                   minutes=int(strTZ[2:]))
     else:
         dateTime = date_utils.subtract(dateTime,
                                        hours=int(strTZ[:2]),
                                        minutes=int(strTZ[2:]))
     return dateTime
示例#3
0
def to_UTCtime(timeStr):
    timeOdoo = fields.Datetime.to_datetime(timeStr[:timeStr.find(".")].replace(
        "T", " "))

    #hh:mm
    utcDistance = timeStr[-4:]

    utcOp = timeStr[-5]

    if utcOp == "+":
        timeOdoo = date_utils.subtract(timeOdoo,
                                       hours=int(utcDistance[:2]),
                                       minutes=int(utcDistance[2:]))
    else:
        timeOdoo = date_utils.add(timeOdoo,
                                  hours=int(utcDistance[:2]),
                                  minutes=int(utcDistance[2:]))

    return timeOdoo
示例#4
0
 def _compute_json_replenishment_history(self):
     for replenishment_report in self:
         replenishment_history = []
         today = fields.Datetime.now()
         first_month = subtract(today, month=2)
         date_from, dummy = get_month(first_month)
         dummy, date_to = get_month(today)
         domain = [('product_id', '=', replenishment_report.product_id.id),
                   ('date', '>=', date_from),
                   ('date', '<=', datetime.combine(date_to, time.max)),
                   ('state', '=', 'done'),
                   ('company_id', '=',
                    replenishment_report.orderpoint_id.company_id.id)]
         quantity_by_month_out = self.env['stock.move'].read_group(
             AND([domain, [('location_dest_id.usage', '=', 'customer')]]),
             ['date', 'product_qty'], ['date:month'])
         quantity_by_month_returned = self.env['stock.move'].read_group(
             AND([domain, [('location_id.usage', '=', 'customer')]]),
             ['date', 'product_qty'], ['date:month'])
         quantity_by_month_returned = {
             g['date:month']: g['product_qty']
             for g in quantity_by_month_returned
         }
         for group in quantity_by_month_out:
             month = group['date:month']
             replenishment_history.append({
                 'name':
                 month,
                 'quantity':
                 group['product_qty'] -
                 quantity_by_month_returned.get(month, 0),
                 'uom_name':
                 replenishment_report.product_id.uom_id.display_name,
             })
         replenishment_report.json_replenishment_history = dumps({
             'template':
             'stock.replenishmentHistory',
             'replenishment_history':
             replenishment_history
         })
示例#5
0
 def _get_membership_interval(self, product, date):
     """Get the interval to evaluate as the theoretical membership period.
     :param product: Product that defines the membership
     :param date: date object for the requested date to determine
     the variable period
     :return: A tuple with 2 date objects with the beginning and the
     end of the period
     """
     if product.membership_type == 'fixed':
         return super(AccountInvoiceLine,
                      self)._get_membership_interval(product, date)
     if product.membership_interval_unit == 'days':
         raise exceptions.Warning(
             _("It's not possible to prorate daily periods."))
     if product.membership_interval_unit == 'weeks':
         date_from = date_utils.start_of(date, 'week')
     elif product.membership_interval_unit == 'months':
         date_from = date_utils.start_of(date, 'month')
     elif product.membership_interval_unit == 'years':
         date_from = date_utils.start_of(date, 'year')
     date_to = date_utils.subtract(product._get_next_date(date), days=1)
     return date_from, date_to
    def _online_sync_bank_statement(self, transactions, online_account):
        """
         build a bank statement from a list of transaction and post messages is also post in the online_account of the journal.
         :param transactions: A list of transactions that will be created in the new bank statement.
             The format is : [{
                 'id': online id,                  (unique ID for the transaction)
                 'date': transaction date,         (The date of the transaction)
                 'name': transaction description,  (The description)
                 'amount': transaction amount,     (The amount of the transaction. Negative for debit, positive for credit)
                 'online_partner_information': optional field used to store information on the statement line under the
                    online_partner_information field (typically information coming from plaid/yodlee). This is use to find partner
                    for next statements
             }, ...]
         :param online_account: The online account for this statement
         Return: The number of imported transaction for the journal
        """
        line_to_reconcile = self.env['account.bank.statement.line']
        for journal in online_account.journal_ids:
            # Since the synchronization succeeded, set it as the bank_statements_source of the journal
            journal.sudo().write({'bank_statements_source': 'online_sync'})
            if not transactions:
                continue

            transactions_identifiers = [
                line['online_transaction_identifier'] for line in transactions
            ]
            existing_transactions_ids = self.env[
                'account.bank.statement.line'].search([
                    ('online_transaction_identifier', 'in',
                     transactions_identifiers), ('journal_id', '=', journal.id)
                ])
            existing_transactions = [
                t.online_transaction_identifier
                for t in existing_transactions_ids
            ]

            transactions_partner_information = []
            for transaction in transactions:
                transaction['date'] = fields.Date.from_string(
                    transaction['date'])
                if transaction.get('online_partner_information'):
                    transactions_partner_information.append(
                        transaction['online_partner_information'])

            if transactions_partner_information:
                self._cr.execute(
                    """
                    SELECT p.online_partner_information, p.id FROM res_partner p
                    WHERE p.online_partner_information IN %s
                """, [tuple(transactions_partner_information)])
                partner_id_per_information = dict(self._cr.fetchall())
            else:
                partner_id_per_information = {}

            sorted_transactions = sorted(transactions, key=lambda l: l['date'])
            min_date = date_utils.start_of(sorted_transactions[0]['date'],
                                           'month')
            if journal.bank_statement_creation_groupby == 'week':
                # key is not always the first of month
                weekday = min_date.weekday()
                min_date = date_utils.subtract(min_date, days=weekday)
            max_date = sorted_transactions[-1]['date']
            total = sum([t['amount'] for t in sorted_transactions])

            statements_in_range = self.search([('date', '>=', min_date),
                                               ('journal_id', '=', journal.id)
                                               ])

            # For first synchronization, an opening bank statement is created to fill the missing bank statements
            all_statement = self.search_count([('journal_id', '=', journal.id)
                                               ])
            digits_rounding_precision = journal.currency_id.rounding if journal.currency_id else journal.company_id.currency_id.rounding
            # If there are neither statement and the ending balance != 0, we create an opening bank statement
            if all_statement == 0 and not float_is_zero(
                    online_account.balance - total,
                    precision_rounding=digits_rounding_precision):
                opening_transaction = [(0, 0, {
                    'date':
                    date_utils.subtract(min_date, days=1),
                    'payment_ref':
                    _("Opening statement: first synchronization"),
                    'amount':
                    online_account.balance - total,
                })]
                op_stmt = self.create({
                    'date':
                    date_utils.subtract(min_date, days=1),
                    'line_ids':
                    opening_transaction,
                    'journal_id':
                    journal.id,
                    'balance_end_real':
                    online_account.balance - total,
                })
                op_stmt.button_post()
                line_to_reconcile += op_stmt.mapped('line_ids')

            transactions_in_statements = []
            statement_to_reset_to_draft = self.env['account.bank.statement']
            transactions_to_create = {}

            for transaction in sorted_transactions:
                if transaction[
                        'online_transaction_identifier'] in existing_transactions:
                    continue  # Do nothing if the transaction already exists
                line = transaction.copy()
                line['online_account_id'] = online_account.id
                if journal.bank_statement_creation_groupby == 'day':
                    # key is full date
                    key = transaction['date']
                elif journal.bank_statement_creation_groupby == 'week':
                    # key is first day of the week
                    weekday = transaction['date'].weekday()
                    key = date_utils.subtract(transaction['date'],
                                              days=weekday)
                elif journal.bank_statement_creation_groupby == 'bimonthly':
                    if transaction['date'].day >= 15:
                        # key is the 15 of that month
                        key = transaction['date'].replace(day=15)
                    else:
                        # key if the first of the month
                        key = date_utils.start_of(transaction['date'], 'month')
                    # key is year-month-0 or year-month-1
                elif journal.bank_statement_creation_groupby == 'month':
                    # key is first of the month
                    key = date_utils.start_of(transaction['date'], 'month')
                else:
                    # key is last date of transactions fetched
                    key = max_date

                # Find partner id if exists
                if line.get('online_partner_information'):
                    partner_info = line['online_partner_information']
                    if partner_id_per_information.get(partner_info):
                        line['partner_id'] = partner_id_per_information[
                            partner_info]

                # Decide if we have to update an existing statement or create a new one with this line
                stmt = statements_in_range.filtered(lambda x: x.date == key)
                if stmt:
                    line['statement_id'] = stmt[0].id
                    transactions_in_statements.append(line)
                    statement_to_reset_to_draft += stmt[0]
                else:
                    if not transactions_to_create.get(key):
                        transactions_to_create[key] = []
                    transactions_to_create[key].append((0, 0, line))

            # Create the lines that should be inside an existing bank statement and reset those stmt in draft
            if transactions_in_statements:
                for st in statement_to_reset_to_draft:
                    if st.state != 'open':
                        st.message_post(body=_(
                            'Statement has been reset to draft because some transactions from online synchronization were added to it.'
                        ))
                statement_to_reset_to_draft.write({'state': 'open'})
                line_to_reconcile += self.env[
                    'account.bank.statement.line'].create(
                        transactions_in_statements)
                # Recompute the balance_end_real of the first statement where we added line
                # because adding line don't trigger a recompute and balance_end_real is not updated.
                # We only trigger the recompute on the first element of the list as it is the one
                # the most in the past and this will trigger the recompute of all the statements
                # that are next.
                statement_to_reset_to_draft[0]._compute_ending_balance()

            # Create lines inside new bank statements
            created_stmts = self.env['account.bank.statement']
            for date, lines in transactions_to_create.items():
                # balance_start and balance_end_real will be computed automatically
                if journal.bank_statement_creation_groupby in ('bimonthly',
                                                               'week',
                                                               'month'):
                    end_date = date
                    if journal.bank_statement_creation_groupby == 'month':
                        end_date = date_utils.end_of(date, 'month')
                    elif journal.bank_statement_creation_groupby == 'week':
                        end_date = date_utils.add(date, days=6)
                    elif journal.bank_statement_creation_groupby == 'bimonthly':
                        if end_date.day == 1:
                            end_date = date.replace(day=14)
                        else:
                            end_date = date_utils.end_of(date, 'month')
                created_stmts += self.env['account.bank.statement'].create({
                    'date':
                    date,
                    'line_ids':
                    lines,
                    'journal_id':
                    journal.id,
                })

            created_stmts.button_post()
            line_to_reconcile += created_stmts.mapped('line_ids')
            # write account balance on the last statement of the journal
            # That way if there are missing transactions, it will show in the last statement
            # and the day missing transactions are fetched or manually written, everything will be corrected
            last_bnk_stmt = self.search([('journal_id', '=', journal.id)],
                                        limit=1)
            if last_bnk_stmt and (created_stmts or transactions_in_statements):
                last_bnk_stmt.balance_end_real = online_account.balance
            # Set last sync date as the last transaction date
            journal.account_online_account_id.sudo().write(
                {'last_sync': max_date})
        return line_to_reconcile
示例#7
0
    def get_production_schedule_view_state(self):
        """ Prepare and returns the fields used by the MPS client action.
        For each schedule returns the fields on the model. And prepare the cells
        for each period depending the manufacturing period set on the company.
        The forecast cells contains the following information:
        - forecast_qty: Demand forecast set by the user
        - date_start: First day of the current period
        - date_stop: Last day of the current period
        - replenish_qty: The quantity to replenish for the current period. It
        could be computed or set by the user.
        - replenish_qty_updated: The quantity to replenish has been set manually
        by the user.
        - starting_inventory_qty: During the first period, the quantity
        available. After, the safety stock from previous period.
        - incoming_qty: The incoming moves and RFQ for the specified product and
        warehouse during the current period.
        - outgoing_qty: The outgoing moves quantity.
        - indirect_demand_qty: On manufacturing a quantity to replenish could
        require a need for a component in another schedule. e.g. 2 product A in
        order to create 1 product B. If the replenish quantity for product B is
        10, it will need 20 product A.
        - safety_stock_qty:
        starting_inventory_qty - forecast_qty - indirect_demand_qty + replenish_qty
        """
        company_id = self.env.company
        date_range = company_id._get_date_range()

        # We need to get the schedule that impact the schedules in self. Since
        # the state is not saved, it needs to recompute the quantity to
        # replenish of finished products. It will modify the indirect
        # demand and replenish_qty of schedules in self.
        schedules_to_compute = self.env['mrp.production.schedule'].browse(self.get_impacted_schedule()) | self

        # Dependencies between schedules
        indirect_demand_trees = schedules_to_compute._get_indirect_demand_tree()
        # Get the schedules that do not depends from other in first position in
        # order to compute the schedule state only once.
        indirect_demand_order = schedules_to_compute._get_indirect_demand_order(indirect_demand_trees)
        indirect_demand_qty = defaultdict(float)
        incoming_qty, incoming_qty_done = self._get_incoming_qty(date_range)
        outgoing_qty, outgoing_qty_done = self._get_outgoing_qty(date_range)
        read_fields = [
            'forecast_target_qty',
            'min_to_replenish_qty',
            'max_to_replenish_qty',
            'product_id',
        ]
        if self.env.user.has_group('stock.group_stock_multi_warehouses'):
            read_fields.append('warehouse_id')
        if self.env.user.has_group('uom.group_uom'):
            read_fields.append('product_uom_id')
        production_schedule_states = schedules_to_compute.read(read_fields)
        production_schedule_states_by_id = {mps['id']: mps for mps in production_schedule_states}
        for production_schedule in indirect_demand_order:
            # Bypass if the schedule is only used in order to compute indirect
            # demand.
            rounding = production_schedule.product_id.uom_id.rounding
            lead_time = production_schedule._get_lead_times()
            production_schedule_state = production_schedule_states_by_id[production_schedule['id']]
            if production_schedule in self:
                procurement_date = add(fields.Date.today(), days=lead_time)
                precision_digits = max(0, int(-(log10(production_schedule.product_uom_id.rounding))))
                production_schedule_state['precision_digits'] = precision_digits
                production_schedule_state['forecast_ids'] = []
            indirect_demand_ratio = production_schedule._get_indirect_demand_ratio(indirect_demand_trees, schedules_to_compute)

            starting_inventory_qty = production_schedule.product_id.qty_available
            if len(date_range):
                starting_inventory_qty -= incoming_qty_done.get((date_range[0], production_schedule.product_id, production_schedule.warehouse_id), 0.0)
                starting_inventory_qty += outgoing_qty_done.get((date_range[0], production_schedule.product_id, production_schedule.warehouse_id), 0.0)

            for date_start, date_stop in date_range:
                forecast_values = {}
                key = ((date_start, date_stop), production_schedule.product_id, production_schedule.warehouse_id)
                existing_forecasts = production_schedule.forecast_ids.filtered(lambda p: p.date >= date_start and p.date <= date_stop)
                if production_schedule in self:
                    forecast_values['date_start'] = date_start
                    forecast_values['date_stop'] = date_stop
                    forecast_values['incoming_qty'] = float_round(incoming_qty.get(key, 0.0) + incoming_qty_done.get(key, 0.0), precision_rounding=rounding)
                    forecast_values['outgoing_qty'] = float_round(outgoing_qty.get(key, 0.0) + outgoing_qty_done.get(key, 0.0), precision_rounding=rounding)

                forecast_values['indirect_demand_qty'] = float_round(indirect_demand_qty.get(key, 0.0), precision_rounding=rounding)
                replenish_qty_updated = False
                if existing_forecasts:
                    forecast_values['forecast_qty'] = float_round(sum(existing_forecasts.mapped('forecast_qty')), precision_rounding=rounding)
                    forecast_values['replenish_qty'] = float_round(sum(existing_forecasts.mapped('replenish_qty')), precision_rounding=rounding)

                    # Check if the to replenish quantity has been manually set or
                    # if it needs to be computed.
                    replenish_qty_updated = any(existing_forecasts.mapped('replenish_qty_updated'))
                    forecast_values['replenish_qty_updated'] = replenish_qty_updated
                else:
                    forecast_values['forecast_qty'] = 0.0

                if not replenish_qty_updated:
                    replenish_qty = production_schedule._get_replenish_qty(starting_inventory_qty - forecast_values['forecast_qty'] - forecast_values['indirect_demand_qty'])
                    forecast_values['replenish_qty'] = float_round(replenish_qty, precision_rounding=rounding)
                    forecast_values['replenish_qty_updated'] = False

                forecast_values['starting_inventory_qty'] = float_round(starting_inventory_qty, precision_rounding=rounding)
                forecast_values['safety_stock_qty'] = float_round(starting_inventory_qty - forecast_values['forecast_qty'] - forecast_values['indirect_demand_qty'] + forecast_values['replenish_qty'], precision_rounding=rounding)

                if production_schedule in self:
                    production_schedule_state['forecast_ids'].append(forecast_values)
                starting_inventory_qty = forecast_values['safety_stock_qty']
                # Set the indirect demand qty for children schedules.
                for (mps, ratio) in indirect_demand_ratio:
                    if not forecast_values['replenish_qty']:
                        continue
                    related_date = max(subtract(date_start, days=lead_time), fields.Date.today())
                    index = next(i for i, (dstart, dstop) in enumerate(date_range) if related_date <= dstart or (related_date >= dstart and related_date <= dstop))
                    related_key = (date_range[index], mps.product_id, mps.warehouse_id)
                    indirect_demand_qty[related_key] += ratio * forecast_values['replenish_qty']

            if production_schedule in self:
                # The state is computed after all because it needs the final
                # quantity to replenish.
                forecasts_state = production_schedule._get_forecasts_state(production_schedule_states_by_id, date_range, procurement_date)
                forecasts_state = forecasts_state[production_schedule.id]
                for index, forecast_state in enumerate(forecasts_state):
                    production_schedule_state['forecast_ids'][index].update(forecast_state)

                # The purpose is to hide indirect demand row if the schedule do not
                # depends from another.
                has_indirect_demand = any(forecast['indirect_demand_qty'] != 0 for forecast in production_schedule_state['forecast_ids'])
                production_schedule_state['has_indirect_demand'] = has_indirect_demand
        return [p for p in production_schedule_states if p['id'] in self.ids]
示例#8
0
# 2019-04-11 01:53:48
# 2019-04-12 01:53:48
# 2019-04-13 01:53:48

date_utils.add(today, days=5)
# 2019-04-03 01:53:48
date_utils.add(today, weeks=2)
# 2019-04-12 01:53:48
date_utils.add(today, months=1)
# 2019-04-29 01:53:48
date_utils.add(today, years=1)
# 2020-03-29 01:53:48
date_utils.add(today, days=2, months=6, years=1)
# 2020-10-01 01:53:48

date_utils.subtract(today, days=5)
# 2019-03-24 01:53:48
date_utils.subtract(today, weeks=2)
# 2019-03-15 01:53:48
date_utils.subtract(today, months=1)
# 2019-02-28 01:53:48
date_utils.subtract(today, years=1)
# 2018-03-29 01:53:48
date_utils.subtract(today, days=2, months=6, years=1)
# 2017-09-27 01:53:48
'''
json_default()
Properly serializes date and datetime objects.
    @api.one
    @api.depends('payment_move_line_ids.amount_residual')
    def _get_payment_info_JSON(self):
    def online_sync_bank_statement(self, transactions, journal,
                                   ending_balance):
        """
         build a bank statement from a list of transaction and post messages is also post in the online_account of the journal.
         :param transactions: A list of transactions that will be created in the new bank statement.
             The format is : [{
                 'id': online id,                  (unique ID for the transaction)
                 'date': transaction date,         (The date of the transaction)
                 'name': transaction description,  (The description)
                 'amount': transaction amount,     (The amount of the transaction. Negative for debit, positive for credit)
                 'partner_id': optional field used to define the partner
                 'online_partner_vendor_name': optional field used to store information on the statement line under the
                    online_partner_vendor_name field (typically information coming from plaid/yodlee). This is use to find partner
                    for next statements
                 'online_partner_bank_account': optional field used to store information on the statement line under the
                    online_partner_bank_account field (typically information coming from plaid/yodlee). This is use to find partner
                    for next statements
             }, ...]
         :param journal: The journal (account.journal) of the new bank statement
         :param ending_balance: ending balance on the account

         Return: The number of imported transaction for the journal
        """
        # Since the synchronization succeeded, set it as the bank_statements_source of the journal
        journal.sudo().write({'bank_statements_source': 'online_sync'})
        if not len(transactions):
            return 0

        transactions_identifiers = [
            line['online_identifier'] for line in transactions
        ]
        existing_transactions_ids = self.env[
            'account.bank.statement.line'].search([
                ('online_identifier', 'in', transactions_identifiers),
                ('journal_id', '=', journal.id)
            ])
        existing_transactions = [
            t.online_identifier for t in existing_transactions_ids
        ]

        sorted_transactions = sorted(transactions, key=lambda l: l['date'])
        min_date = date_utils.start_of(sorted_transactions[0]['date'], 'month')
        if journal.bank_statement_creation == 'week':
            # key is not always the first of month
            weekday = min_date.weekday()
            min_date = date_utils.subtract(min_date, days=weekday)
        max_date = sorted_transactions[-1]['date']
        total = sum([t['amount'] for t in sorted_transactions])

        statements_in_range = self.search([('date', '>=', min_date),
                                           ('journal_id', '=', journal.id)])

        # For first synchronization, an opening bank statement is created to fill the missing bank statements
        all_statement = self.search_count([('journal_id', '=', journal.id)])
        digits_rounding_precision = journal.currency_id.rounding if journal.currency_id else journal.company_id.currency_id.rounding
        if all_statement == 0 and not float_is_zero(
                ending_balance - total,
                precision_rounding=digits_rounding_precision):
            opening_transaction = [(0, 0, {
                'date':
                date_utils.subtract(min_date, days=1),
                'payment_ref':
                _("Opening statement: first synchronization"),
                'amount':
                ending_balance - total,
            })]
            statement = self.create({
                'name':
                _('Opening statement'),
                'date':
                date_utils.subtract(min_date, days=1),
                'line_ids':
                opening_transaction,
                'journal_id':
                journal.id,
                'balance_end_real':
                ending_balance - total,
            })
            statement.button_post()

        transactions_in_statements = []
        statement_to_reset_to_draft = self.env['account.bank.statement']
        transactions_to_create = {}

        number_added = 0
        for transaction in sorted_transactions:
            if transaction['online_identifier'] in existing_transactions:
                continue
            line = transaction.copy()
            number_added += 1
            if journal.bank_statement_creation == 'day':
                # key is full date
                key = transaction['date']
            elif journal.bank_statement_creation == 'week':
                # key is first day of the week
                weekday = transaction['date'].weekday()
                key = date_utils.subtract(transaction['date'], days=weekday)
            elif journal.bank_statement_creation == 'bimonthly':
                if transaction['date'].day >= 15:
                    # key is the 15 of that month
                    key = transaction['date'].replace(day=15)
                else:
                    # key if the first of the month
                    key = date_utils.start_of(transaction['date'], 'month')
                # key is year-month-0 or year-month-1
            elif journal.bank_statement_creation == 'month':
                # key is first of the month
                key = date_utils.start_of(transaction['date'], 'month')
            else:
                # key is last date of transactions fetched
                key = max_date

            # Decide if we have to update an existing statement or create a new one with this line
            stmt = statements_in_range.filtered(lambda x: x.date == key)
            if stmt and stmt[0].id:
                line['statement_id'] = stmt[0].id
                transactions_in_statements.append(line)
                statement_to_reset_to_draft += stmt[0]
            else:
                if not transactions_to_create.get(key):
                    transactions_to_create[key] = []
                transactions_to_create[key].append((0, 0, line))

        # Create the lines that should be inside an existing bank statement and reset those stmt in draft
        if len(transactions_in_statements):
            for st in statement_to_reset_to_draft:
                if st.state == 'confirm':
                    st.message_post(body=_(
                        'Statement has been reset to draft because some transactions from online synchronization were added to it.'
                    ))
                    st.state = 'posted'

            posted_statements = statement_to_reset_to_draft.filtered(
                lambda st: st.state == 'posted')
            posted_statements.state = 'open'
            statement_lines = self.env['account.bank.statement.line'].create(
                transactions_in_statements)
            posted_statements.state = 'posted'

            # Post only the newly created statement lines if the related statement is already posted.
            statement_lines.filtered(lambda line: line.statement_id.state == 'posted')\
                .mapped('move_id')\
                .with_context(skip_account_move_synchronization=True)\
                ._post()

            # Recompute the balance_end_real of the first statement where we added line
            # because adding line don't trigger a recompute and balance_end_real is not updated.
            # We only trigger the recompute on the first element of the list as it is the one
            # the most in the past and this will trigger the recompute of all the statements
            # that are next.
            statement_to_reset_to_draft[0]._compute_ending_balance()

        # Create lines inside new bank statements
        st_vals_list = []
        for date, lines in transactions_to_create.items():
            # balance_start and balance_end_real will be computed automatically
            name = _('Online synchronization of %s') % (date, )
            if journal.bank_statement_creation in ('bimonthly', 'week',
                                                   'month'):
                name = _('Online synchronization from %s to %s')
                end_date = date
                if journal.bank_statement_creation == 'month':
                    end_date = date_utils.end_of(date, 'month')
                elif journal.bank_statement_creation == 'week':
                    end_date = date_utils.add(date, days=6)
                elif journal.bank_statement_creation == 'bimonthly':
                    if end_date.day == 1:
                        end_date = date.replace(day=14)
                    else:
                        end_date = date_utils.end_of(date, 'month')
                name = name % (date, end_date)
            st_vals_list.append({
                'name': name,
                'date': date,
                'line_ids': lines,
                'journal_id': journal.id
            })
        statements = self.env['account.bank.statement'].create(st_vals_list)
        statements.button_post()

        # write account balance on the last statement of the journal
        # That way if there are missing transactions, it will show in the last statement
        # and the day missing transactions are fetched or manually written, everything will be corrected
        last_bnk_stmt = self.search([('journal_id', '=', journal.id)], limit=1)
        if last_bnk_stmt:
            last_bnk_stmt.balance_end_real = ending_balance
            if last_bnk_stmt.state == 'posted' and last_bnk_stmt.balance_end != last_bnk_stmt.balance_end_real:
                last_bnk_stmt.button_reopen()
        # Set last sync date as the last transaction date
        journal.account_online_journal_id.sudo().write({'last_sync': max_date})
        return number_added
    def get_production_schedule_view_state(self):
        company_id = self.env.company
        date_range = company_id._get_date_range()

        schedules_to_compute = self.env['mrp.production.schedule'].browse(
            self.get_impacted_schedule()) | self

        indirect_demand_trees = schedules_to_compute._get_indirect_demand_tree(
        )

        indirect_demand_order = schedules_to_compute._get_indirect_demand_order(
            indirect_demand_trees)
        indirect_demand_qty = defaultdict(float)
        incoming_qty, incoming_qty_done = self._get_incoming_qty(date_range)
        outgoing_qty, outgoing_qty_done = self._get_outgoing_qty(date_range)
        read_fields = [
            'forecast_target_qty',
            'min_to_replenish_qty',
            'max_to_replenish_qty',
            'product_id',
        ]
        if self.env.user.has_group('stock.group_stock_multi_warehouses'):
            read_fields.append('warehouse_id')
        if self.env.user.has_group('uom.group_uom'):
            read_fields.append('product_uom_id')
        production_schedule_states = schedules_to_compute.read(read_fields)
        production_schedule_states_by_id = {
            mps['id']: mps
            for mps in production_schedule_states
        }
        for production_schedule in indirect_demand_order:
            # Bypass if the schedule is only used in order to compute indirect
            # demand.

            ########################################
            data = self.moq_of_product(production_schedule.product_tmpl_id)
            quantity_week = data['quantity_week']
            moq = data['moq']
            unidad_redondeo = data['unidad_redondeo']
            ########################################

            rounding = production_schedule.product_id.uom_id.rounding
            lead_time = production_schedule._get_lead_times()
            production_schedule_state = production_schedule_states_by_id[
                production_schedule['id']]
            if production_schedule in self:
                procurement_date = add(fields.Date.today(), days=lead_time)
                precision_digits = max(
                    0,
                    int(-(log10(production_schedule.product_uom_id.rounding))))
                production_schedule_state[
                    'precision_digits'] = precision_digits
                production_schedule_state['forecast_ids'] = []
            indirect_demand_ratio = production_schedule._get_indirect_demand_ratio(
                indirect_demand_trees, schedules_to_compute)

            starting_inventory_qty = production_schedule.product_id.with_context(
                warehouse=production_schedule.warehouse_id.id).qty_available
            if len(date_range):
                starting_inventory_qty -= incoming_qty_done.get(
                    (date_range[0], production_schedule.product_id,
                     production_schedule.warehouse_id), 0.0)
                starting_inventory_qty += outgoing_qty_done.get(
                    (date_range[0], production_schedule.product_id,
                     production_schedule.warehouse_id), 0.0)

            pos = 0
            for date_start, date_stop in date_range:
                forecast_values = {}
                key = ((date_start, date_stop), production_schedule.product_id,
                       production_schedule.warehouse_id)
                existing_forecasts = production_schedule.forecast_ids.filtered(
                    lambda p: p.date >= date_start and p.date <= date_stop)
                if production_schedule in self:
                    forecast_values['date_start'] = date_start
                    forecast_values['date_stop'] = date_stop
                    forecast_values['incoming_qty'] = float_round(
                        incoming_qty.get(key, 0.0) +
                        incoming_qty_done.get(key, 0.0),
                        precision_rounding=rounding)
                    forecast_values['outgoing_qty'] = float_round(
                        outgoing_qty.get(key, 0.0) +
                        outgoing_qty_done.get(key, 0.0),
                        precision_rounding=rounding)

                forecast_values['indirect_demand_qty'] = float_round(
                    indirect_demand_qty.get(key, 0.0),
                    precision_rounding=rounding)
                replenish_qty_updated = False
                if existing_forecasts:
                    forecast_values['forecast_qty'] = float_round(
                        sum(existing_forecasts.mapped('forecast_qty')),
                        precision_rounding=rounding)
                    forecast_values['replenish_qty'] = float_round(
                        sum(existing_forecasts.mapped('replenish_qty')),
                        precision_rounding=rounding)

                    # Check if the to replenish quantity has been manually set or
                    # if it needs to be computed.
                    replenish_qty_updated = any(
                        existing_forecasts.mapped('replenish_qty_updated'))
                    forecast_values[
                        'replenish_qty_updated'] = replenish_qty_updated
                else:
                    forecast_values['forecast_qty'] = 0.0

                # if not replenish_qty_updated:
                #     replenish_qty = production_schedule._get_replenish_qty(starting_inventory_qty - forecast_values['forecast_qty'] - forecast_values['indirect_demand_qty'])
                #     forecast_values['replenish_qty'] = float_round(replenish_qty, precision_rounding=rounding)
                #     forecast_values['replenish_qty_updated'] = False

                ########################################
                if replenish_qty_updated:
                    if (pos >= quantity_week
                            and forecast_values['replenish_qty'] > 0
                            and forecast_values['replenish_qty'] < moq):
                        forecast_values['replenish_qty'] = moq
                else:
                    if (pos < quantity_week
                            and forecast_values.get('replenish_qty', 0) == 0):
                        forecast_values['replenish_qty'] = 0
                    else:
                        replenish_qty = production_schedule._get_replenish_qty(
                            starting_inventory_qty -
                            forecast_values['forecast_qty'] -
                            forecast_values['indirect_demand_qty'])
                        if unidad_redondeo > 0 and (replenish_qty %
                                                    unidad_redondeo) > 0:
                            resto = unidad_redondeo - (replenish_qty %
                                                       unidad_redondeo)
                            replenish_qty = replenish_qty + resto

                        replenish_qty = float_round(
                            replenish_qty, precision_rounding=rounding)
                        if (replenish_qty > 0 and replenish_qty < moq):
                            forecast_values['replenish_qty'] = moq
                        else:
                            forecast_values['replenish_qty'] = float_round(
                                replenish_qty, precision_rounding=rounding)
                        forecast_values['replenish_qty_updated'] = False

                pos = pos + 1
                ########################################

                forecast_values['starting_inventory_qty'] = float_round(
                    starting_inventory_qty, precision_rounding=rounding)
                forecast_values['safety_stock_qty'] = float_round(
                    starting_inventory_qty - forecast_values['forecast_qty'] -
                    forecast_values['indirect_demand_qty'] +
                    forecast_values['replenish_qty'],
                    precision_rounding=rounding)

                if production_schedule in self:
                    production_schedule_state['forecast_ids'].append(
                        forecast_values)
                starting_inventory_qty = forecast_values['safety_stock_qty']
                # Set the indirect demand qty for children schedules.
                for (mps, ratio) in indirect_demand_ratio:
                    if not forecast_values['replenish_qty']:
                        continue
                    related_date = max(subtract(date_start, days=lead_time),
                                       fields.Date.today())
                    index = next(
                        i for i, (dstart, dstop) in enumerate(date_range)
                        if related_date <= dstart or (
                            related_date >= dstart and related_date <= dstop))
                    related_key = (date_range[index], mps.product_id,
                                   mps.warehouse_id)
                    indirect_demand_qty[
                        related_key] += ratio * forecast_values['replenish_qty']

            if production_schedule in self:
                # The state is computed after all because it needs the final
                # quantity to replenish.
                forecasts_state = production_schedule._get_forecasts_state(
                    production_schedule_states_by_id, date_range,
                    procurement_date)
                forecasts_state = forecasts_state[production_schedule.id]
                for index, forecast_state in enumerate(forecasts_state):

                    ########################################
                    if (index < quantity_week):
                        forecast_state['state'] = 'to_custom'
                    ########################################

                    production_schedule_state['forecast_ids'][index].update(
                        forecast_state)

                # The purpose is to hide indirect demand row if the schedule do not
                # depends from another.
                has_indirect_demand = any(
                    forecast['indirect_demand_qty'] != 0
                    for forecast in production_schedule_state['forecast_ids'])
                production_schedule_state[
                    'has_indirect_demand'] = has_indirect_demand
        return [p for p in production_schedule_states if p['id'] in self.ids]