Ejemplo n.º 1
0
 def _onchange_predefined_date(self):
     today = fields.Date.context_today(self)
     if self.predefined_date == 'day':
         self.snoozed_until = add(today, days=1)
     elif self.predefined_date == 'week':
         self.snoozed_until = add(today, weeks=1)
     elif self.predefined_date == 'month':
         self.snoozed_until = add(today, months=1)
Ejemplo n.º 2
0
 def _get_next_date(self, date, qty=1):
     next_date = super(ProductTemplate, self)._get_next_date(date)
     if self.membership_interval_unit == 'days':
         raise exceptions.Warning(
             _("It's not possible to prorate daily periods."))
     qty = math.ceil(qty) * self.membership_interval_qty
     if self.membership_interval_unit == 'weeks':
         next_date = date_utils.start_of(date, 'week')
         next_date = date_utils.add(next_date, weeks=qty)
     elif self.membership_interval_unit == 'months':
         next_date = date_utils.start_of(date, 'month')
         next_date = date_utils.add(next_date, months=qty)
     elif self.membership_interval_unit == 'years':
         next_date = date_utils.start_of(date, 'year')
         next_date = date_utils.add(next_date, years=qty)
     return next_date
Ejemplo n.º 3
0
    def _onchange_employee(self):
        if (not self.employee_id) or (not self.date_from) or (not self.date_to):
            return

        employee = self.employee_id
        date_from = self.date_from
        date_to = self.date_to

        self.company_id = employee.company_id
        if not self.contract_id or self.employee_id != self.contract_id.employee_id: # Add a default contract if not already defined
            contracts = employee._get_contracts(date_from, date_to)

            if not contracts or not contracts[0].structure_type_id.default_struct_id:
                self.contract_id = False
                self.struct_id = False
                return
            self.contract_id = contracts[0]
            self.struct_id = contracts[0].structure_type_id.default_struct_id

        payslip_name = self.struct_id.payslip_name or _('Salary Slip')
        self.name = '%s - %s - %s' % (payslip_name, self.employee_id.name or '', format_date(self.env, self.date_from, date_format="MMMM y"))

        if date_to > date_utils.end_of(fields.Date.today(), 'month'):
            self.warning_message = _("This payslip can be erroneous! Work entries may not be generated for the period from %s to %s." %
                (date_utils.add(date_utils.end_of(fields.Date.today(), 'month'), days=1), date_to))
        else:
            self.warning_message = False

        self.worked_days_line_ids = self._get_new_worked_days_lines()
Ejemplo n.º 4
0
    def _onchange_employee(self):
        if (not self.employee_id) or (not self.date_from) or (
                not self.date_to):
            return

        employee = self.employee_id
        date_from = self.date_from
        date_to = self.date_to

        self.company_id = employee.company_id
        if not self.contract_id or self.employee_id != self.contract_id.employee_id:  # Add a default contract if not already defined
            contracts = employee._get_contracts(date_from, date_to)
            # if contracts.structure_type_id.name == 'Base':
            #   structure = self.env['hr.payroll.structure'].search([('type_id','=','Base')])
            #   for struct in structure:
            #       for rule in struct.rule_ids:
            #           if rule.code == 'GROSS':
            #               rule.unlink()
            #           if rule.code == 'NET':
            #               rule.unlink()
            # raise UserError(str("stucture"))

            if not contracts or not contracts[
                    0].structure_type_id.default_struct_id:
                self.contract_id = False
                self.struct_id = False
                return
            self.contract_id = contracts[0]
            self.struct_id = contracts[0].structure_type_id.default_struct_id

        payslip_name = self.struct_id.payslip_name or _('Salary Slip')
        self.name = '%s - %s - %s' % (
            payslip_name, self.employee_id.name
            or '', format_date(self.env, self.date_from, date_format="MMMM y"))

        if date_to > date_utils.end_of(fields.Date.today(), 'month'):
            self.warning_message = _(
                "This payslip can be erroneous! Work entries may not be generated for the period from %s to %s."
                % (date_utils.add(date_utils.end_of(fields.Date.today(),
                                                    'month'),
                                  days=1), date_to))
        else:
            self.warning_message = False

        self.worked_days_line_ids = self._get_new_worked_days_lines()
        input_id = self.env['hr.payslip.input'].search([('payslip_id', '=',
                                                         self.struct_id.id)])
        input_line_ids = self._get_inputs()
        input_lines = self.input_line_ids.browse([])
        # raise UserError(str(input_lines))
        for r in input_line_ids:
            # raise UserError(str(r))
            input_lines += input_lines.new(r)
            # raise UserError(str(input_lines))
        self.input_line_ids = input_lines
        # raise UserError(str(input_line_ids))
        return
Ejemplo n.º 5
0
    def _xerox_get_records(self, company_id, run_date):

        res = super()._xerox_get_records(company_id, run_date)
        next_date = date_utils.add(run_date, days=1)
        res['fsm.route.dayroute'] = self.env["fsm.route.dayroute"].search([
            ("stage_id.is_closed", "=", "True"),
            ("date_close", ">=", run_date),
            ("date_close", "<", next_date),
        ])
        return res
Ejemplo n.º 6
0
 def _get_date_range(self):
     """ Return the date range for a production schedude depending the
     manufacturing period and the number of columns to display specify by the
     user. It returns a list of tuple that contains the timestamp for each
     column.
     """
     self.ensure_one()
     date_range = []
     first_day = start_of(fields.Date.today(), self.manufacturing_period)
     for columns in range(self.manufacturing_period_to_display):
         last_day = end_of(first_day, self.manufacturing_period)
         date_range.append((first_day, last_day))
         first_day = add(last_day, days=1)
     return date_range
Ejemplo n.º 7
0
    def _get_date_range(self):
        date_range = []
        first_day = start_of(fields.Date.today(), self.env.company.manufacturing_period)
        manufacturing_period = self.env.company.manufacturing_period_to_display
        week = (first_day.day // 7) + 1
        
        for i in range(week):
            first_day = first_day + relativedelta(days=-7)
            manufacturing_period = manufacturing_period + 1

        for columns in range(manufacturing_period):
            last_day = end_of(first_day, self.env.company.manufacturing_period)
            date_range.append((first_day, last_day))
            first_day = add(last_day, days=1)
        return date_range
Ejemplo n.º 8
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
Ejemplo n.º 9
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
Ejemplo n.º 10
0
    def cron_xerox_send_files(self, run_date=None):
        """
        Run Cron Scheduler action.
        Accepts an optional run date,
        that can be either a Date object or a string.
        This can be used to manually run tests for a particualr day.
        """
        if not run_date:
            # Use yesterday
            today = fields.Date.context_today(self)
            run_date = date_utils.add(today, days=-1)
        elif not isinstance(run_date, date):
            # Convert t Date object
            run_date = fields.Date.to_date(run_date)

        companies = self.env["res.company"].search(
            [
                ("backend_acp_id", "!=", False),
                ("backend_acp_id.status", "=", 'confirmed'),
                ("backend_acp_id.send_immediately", "=", False),
            ]
        )
        if not companies:
            _logger.info('No Company is configured for Xerox integration')

        for company in companies:
            ungrouped_rsets = self._xerox_get_records(company, run_date)
            doc_count = sum(len(x) for x in ungrouped_rsets.values())
            _logger.info(
                'Found %d documents for company %s', doc_count, company.name)
            # Group records by file set, using rule in `xerox_group()`
            grouped_data = {}
            for rset in ungrouped_rsets.values():
                # In place update of the grouped data dict
                grouped_data = rset.xerox_group(grouped_data)

            file_dict = {}
            for key, records in grouped_data.items():
                for record in records:
                    # FIXME: run .with_delay. Implies some code reorg
                    file_dict = record.build_files(file_dict)
            company.backend_acp_id.send(file_dict)
Ejemplo n.º 11
0
 def _xerox_get_records(self, company_id, run_date):
     """Find and returns all documents."""
     next_date = date_utils.add(run_date, days=1)
     class_ids = self.env["sii.document.class"].search([
         ("dte", "=", True)
     ]).ids
     recs = {}
     recs['account.invoice'] = self.env["account.invoice"].search([
         ("date_invoice", "=", run_date),
         ("state", "in", ["open", "paid"]),
         ("class_id", "in", class_ids)
     ])
     recs['stock.picking'] = self.env["stock.picking"].search([
         ("picking_type_code", "=", "outgoing"),
         ("state", "=", "done"),
         ("date_done", ">=", run_date),
         ("date_done", "<=", next_date),
         ("class_id", "in", class_ids)
     ])
     return recs
Ejemplo n.º 12
0
    def action_open_moves(self, product_id, state, date):
        date = datetime.strptime(date, '%Y-%m-%d')
        product = self.env['product.product'].browse(product_id)
        domain = [('product_id', '=', product_id)]
        if state in ('in', 'out'):
            domain = expression.AND([domain, [('date_expected', '>=', date)]])
        domain = expression.AND(
            [domain, [('date_expected', '<', date_utils.add(date, days=1))]])
        domain = expression.AND(
            [domain, [('state', 'not in', ['draft', 'cancel', 'done'])]])
        internal, loc_domain_in, loc_domain_out = product._get_domain_locations(
        )
        if state == 'in':
            loc_domain = loc_domain_in
        elif state == 'out':
            loc_domain = loc_domain_out
        else:
            loc_domain = expression.OR([loc_domain_in, loc_domain_out])
        domain = expression.AND([domain, loc_domain])

        return {
            'type':
            'ir.actions.act_window',
            'view_mode':
            'tree',
            'res_model':
            'stock.move',
            'name':
            '%s %s' % (product.display_name, state),
            'views': [
                (self.env.ref(
                    'stock_enterprise.stock_enterprise_move_tree_view').id,
                 'list'),
            ],
            'domain':
            domain,
            'context':
            self.env.context,
        }
Ejemplo n.º 13
0
 def _xerox_get_domain_picking(self,
                               run_date=None,
                               force=False,
                               picking_type="outgoing"):
     domain = [
         ("picking_type_code", "=", picking_type),
         # Deliveries are signed once they are waiting or confirmed
         # They will ony be "done" when delivered at customer site
         ("state", "not in", ("draft", "cancel")),
         ("class_id", "!=", False),
         ("class_id.dte", "=", True),
     ]
     code = self.env.context.get('xerox', False)
     if code:
         domain.append(("class_id.code", "=", code))
     if run_date:
         run_date1 = date_utils.add(run_date, days=1)
         domain.extend([
             ("scheduled_date", ">=", run_date),
             ("scheduled_date", "<", run_date1),
         ])
     if not force:
         domain.append(("date_sign", "=", False))
     return domain
    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
Ejemplo n.º 15
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]
Ejemplo n.º 16
0
date_utils.end_of(today, 'hour')
# 2019-03-29 01:59:59.999999
date_utils.end_of(today, 'day')
# 2019-03-29 23:59:59.999999
date_utils.end_of(today, 'week')
# 2019-03-31 23:59:59.999999
date_utils.end_of(today, 'month')
# 2019-03-31 23:59:59.999999
date_utils.end_of(today, 'quarter')
# 2019-03-31 23:59:59.999999
date_utils.end_of(today, 'year')
# 2019-12-31 23:59:59.999999

for date in date_utils.date_range(start=today,
                                  end=date_utils.add(today, days=15),
                                  step=relativedelta(days=1)):
    print(date)
# 2019-03-29 01:53:48
# 2019-03-30 01:53:48
# 2019-03-31 01:53:48
# 2019-04-01 01:53:48
# 2019-04-02 01:53:48
# 2019-04-03 01:53:48
# 2019-04-04 01:53:48
# 2019-04-05 01:53:48
# 2019-04-06 01:53:48
# 2019-04-07 01:53:48
# 2019-04-08 01:53:48
# 2019-04-09 01:53:48
# 2019-04-10 01:53:48
    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]
Ejemplo n.º 18
0
    def onchange_employee(self):
        if (not self.employee_id) or (not self.date_from) or (
                not self.date_to):
            return

        employee = self.employee_id
        date_from = self.date_from
        date_to = self.date_to
        contract_ids = []
        contracts = self.env['hr.contract']

        ttyme = datetime.fromtimestamp(
            time.mktime(time.strptime(str(date_from), "%Y-%m-%d")))
        locale = self.env.context.get('lang') or 'en_US'
        self.name = _('Salary Slip of %s for %s') % (
            employee.name,
            tools.ustr(
                babel.dates.format_date(
                    date=ttyme, format='MMMM-y', locale=locale)))
        self.company_id = employee.company_id
        if not self.contract_id or self.employee_id != self.contract_id.employee_id:  # Add a default contract if not already defined
            contracts = employee._get_contracts(date_from, date_to)

            if not contracts or not contracts[
                    0].structure_type_id.default_struct_id:
                self.contract_id = False
                self.struct_id = False
                return
            self.contract_id = contracts[0]
            self.struct_id = contracts[0].structure_type_id.default_struct_id
        if date_to > date_utils.end_of(fields.Date.today(), 'month'):
            self.warning_message = _(
                "This payslip can be erroneous! Work entries may not be generated for the period from %s to %s."
                % (date_utils.add(date_utils.end_of(fields.Date.today(),
                                                    'month'),
                                  days=1), date_to))
        else:
            self.warning_message = False

        self.worked_days_line_ids = self._get_new_worked_days_lines()

        # if not self.env.context.get('contract') or not self.contract_id:
        #     contract_ids = self.get_contract(employee, date_from, date_to)
        #     if not contract_ids:
        #         return
        #     self.contract_id = self.env['hr.contract'].browse(contract_ids[0])
        #
        # if not self.contract_id.struct_id:
        #     return
        # self.struct_id = self.contract_id.struct_id
        #
        # # computation of the salary input
        # contracts = self.env['hr.contract'].browse(contract_ids)
        # worked_days_line_ids = self.get_worked_day_lines(contracts, date_from, date_to)
        # worked_days_lines = self.worked_days_line_ids.browse([])
        # for r in worked_days_line_ids:
        #     worked_days_lines += worked_days_lines.new(r)
        # self.worked_days_line_ids = worked_days_lines
        # if len(contracts) > 0:
        #     input_line_ids = self.get_inputs(contracts, date_from, date_to)
        #     input_lines = self.input_line_ids.browse([])
        #     for r in input_line_ids:
        #         input_lines += input_lines.new(r)
        #     self.input_line_ids = input_lines
        lon_obj = self.env['hr.loan'].search([('employee_id', '=',
                                               employee.id),
                                              ('state', '=', 'approve')])
        # _logger.info("<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>")
        # _logger.info(res)
        # _logger.info(contract_ids)
        # _logger.info(lon_obj)
        # _logger.info("<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>")
        for loan in lon_obj:
            for loan_line in loan.loan_lines:
                if date_from <= loan_line.date <= date_to and not loan_line.paid:
                    payslip_other_input_type = self.env[
                        'hr.payslip.input.type'].search([('code', '=', 'LO')],
                                                        limit=1)
                    if self.changed_get_loan:
                        pass
                    else:
                        self.input_line_ids = [(0, 0, {
                            'input_type_id':
                            payslip_other_input_type.id,
                            'amount':
                            loan_line.amount,
                            'loan_line_id':
                            loan_line.id
                        })]
                        self.changed_get_loan = True

                    # for result in res:
                    #     if result.get('code') == 'LO':
                    #         result['amount'] = loan_line.amount
                    #         result['loan_line_id'] = loan_line.id

        return
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
 def _compute_expected_date(self):
     for record in self:
         record.expected_date = False
         if record.check_in:
             record.expected_date = date_utils.add(record.check_in,
                                                   days=record.expected_day)