def _l10n_it_edi_upload(self, files, proxy_user): '''Upload files to fatturapa. :param files: A list of dictionary {filename, base64_xml}. :returns: A dictionary. * message: Message from fatturapa. * transactionId: The fatturapa ID of this request. * error: An eventual error. * error_level: Info, warning, error. ''' ERRORS = { 'EI01': {'error': _lt('Attached file is empty'), 'blocking_level': 'error'}, 'EI02': {'error': _lt('Service momentarily unavailable'), 'blocking_level': 'warning'}, 'EI03': {'error': _lt('Unauthorized user'), 'blocking_level': 'error'}, } server_url = self.env['ir.config_parameter'].get_param('account_edi_proxy_client.edi_server_url', DEFAULT_SERVER_URL) result = proxy_user._make_request(server_url + '/api/l10n_it_edi/1/out/SdiRiceviFile', params={'files': files}) # Translate the errors. for filename in result.keys(): if 'error' in result[filename]: result[filename] = ERRORS.get(result[filename]['error'], {'error': result[filename]['error'], 'blocking_level': 'error'}) return result
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('mrp.group_mrp_user'): buttons.extend([{ 'icon': 'wrench', 'text': _lt('Manufacturing Orders'), 'number': self.production_count, 'action_type': 'object', 'action': 'action_view_mrp_production', 'show': self.production_count > 0, 'sequence': 39, }, { 'icon': 'cog', 'text': _lt('Work Orders'), 'number': self.workorder_count, 'action_type': 'object', 'action': 'action_view_workorder', 'show': self.workorder_count > 0, 'sequence': 42, }, { 'icon': 'flask', 'text': _lt('Bills of Materials'), 'number': self.bom_count, 'action_type': 'object', 'action': 'action_view_mrp_bom', 'show': self.bom_count > 0, 'sequence': 45, }]) return buttons
def _get_profitability_labels(self): return { **super()._get_profitability_labels(), 'service_revenues': _lt('Other Services'), 'other_revenues': _lt('Material'), }
def _get_profitability_labels(self): return { **super()._get_profitability_labels(), 'billable_fixed': _lt('Timesheets (Fixed Price)'), 'billable_time': _lt('Timesheets (Billed on Timesheets)'), 'billable_milestones': _lt('Timesheets (Billed on Milestones)'), 'billable_manual': _lt('Timesheets (Billed Manually)'), 'non_billable': _lt('Timesheets (Non Billable)'), }
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('hr_timesheet.group_hr_timesheet_approver'): buttons.append({ 'icon': 'clock-o', 'text': _lt('Billable Time'), 'number': '%s %%' % (self.billable_percentage), 'action_type': 'object', 'action': 'action_billable_time_button', 'additional_context': json.dumps({ 'active_id': self.id, 'default_project_id': self.id }), 'show': self.allow_timesheets and bool(self.analytic_account_id), 'sequence': 9, }) return buttons
def _get_sale_order_stat_button(self): self.ensure_one() return { 'icon': 'dollar', 'text': _lt('Sales Order'), 'action_type': 'object', 'action': 'action_view_so', 'show': bool(self.sale_order_id), 'sequence': 1, }
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('sales_team.group_sale_salesman_all_leads'): buttons.append({ 'icon': 'dollar', 'text': _lt('Sales Orders'), 'number': self.sale_order_count, 'action_type': 'object', 'action': 'action_view_sos', 'show': self.sale_order_count > 0, 'sequence': 1, }) if self.user_has_groups('account.group_account_readonly'): buttons.append({ 'icon': 'pencil-square-o', 'text': _lt('Invoices'), 'number': self.invoice_count, 'action_type': 'object', 'action': 'action_open_project_invoices', 'show': bool(self.analytic_account_id) and self.invoice_count > 0, 'sequence': 30, }) if self.user_has_groups('account.group_account_readonly'): buttons.append({ 'icon': 'pencil-square-o', 'text': _lt('Vendor Bills'), 'number': self.vendor_bill_count, 'action_type': 'object', 'action': 'action_open_project_vendor_bills', 'show': self.vendor_bill_count > 0, 'sequence': 48, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('purchase.group_purchase_user'): buttons.append({ 'icon': 'credit-card', 'text': _lt('Purchase Orders'), 'number': self.purchase_orders_count, 'action_type': 'object', 'action': 'action_open_project_purchase_orders', 'show': self.purchase_orders_count > 0, 'sequence': 36, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('hr_expense.group_hr_expense_team_approver'): buttons.append({ 'icon': 'money', 'text': _lt('Expenses'), 'number': self.expenses_count, 'action_type': 'object', 'action': 'action_open_project_expenses', 'show': self.expenses_count > 0, 'sequence': 33, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('hr_timesheet.group_hr_timesheet_user'): buttons.append({ 'icon': 'clock-o', 'text': _lt('Recorded'), 'number': '%s %s' % (self.total_timesheet_time, self.env.company.timesheet_encode_uom_id.name), 'action_type': 'object', 'action': 'action_show_timesheets_by_employee_invoice_type', 'show': self.allow_timesheets, 'sequence': 6, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('mrp.group_mrp_user'): buttons.extend([{ 'icon': 'flask', 'text': _lt('Bills of Materials'), 'number': self.bom_count, 'action_type': 'object', 'action': 'action_view_mrp_bom', 'show': self.bom_count > 0, 'sequence': 45, }]) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('account.group_account_readonly'): buttons.append({ 'icon': 'pencil-square-o', 'text': _lt('Vendor Bills'), 'number': self.vendor_bill_count, 'action_type': 'object', 'action': 'action_open_project_vendor_bills', 'show': self.vendor_bill_count > 0, 'sequence': 48, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('sales_team.group_sale_salesman_all_leads'): buttons.append({ 'icon': 'dollar', 'text': _lt('Sales Orders'), 'number': self.sale_order_count, 'action_type': 'object', 'action': 'action_view_sos', 'show': self.sale_order_count > 0, 'sequence': 1, }) return buttons
def _get_stat_buttons(self): buttons = super(Project, self)._get_stat_buttons() if self.user_has_groups('account.group_account_readonly'): buttons.append({ 'icon': 'pencil-square-o', 'text': _lt('Invoices'), 'number': self.invoice_count, 'action_type': 'object', 'action': 'action_open_project_invoices', 'show': bool(self.analytic_account_id) and self.invoice_count > 0, 'sequence': 30, }) return buttons
class AccountStudentLedger(models.AbstractModel): _inherit = "account.report" _name = "account.student.ledger" _description = "Student Ledger" filter_date = {'mode': 'range', 'filter': 'this_year'} filter_all_entries = False filter_unfold_all = False filter_account_type = [ { 'id': 'receivable', 'name': _lt('Receivable'), 'selected': False }, { 'id': 'payable', 'name': _lt('Payable'), 'selected': False }, ] filter_unreconciled = False filter_partner = True filter_family = True #################################################### # OPTIONS #################################################### @api.model def _init_filter_partner(self, options, previous_options=None): if not self.filter_partner: return super(AccountStudentLedger, self)._init_filter_partner(options, previous_options) options['family_ids'] = previous_options and previous_options.get( 'family_ids') or [] selected_family_ids = [int(family) for family in options['family_ids']] selected_families = selected_family_ids and self.env[ 'res.partner'].browse( selected_family_ids) or self.env['res.partner'] options['selected_family_ids'] = selected_families.mapped('name') options['grade_level_ids'] = previous_options and previous_options.get( 'grade_level_ids') or [] selected_grade_level_ids = [ int(category) for category in options['grade_level_ids'] ] selected_grade_levels = selected_grade_level_ids and self.env[ 'school_base.grade_level'].browse( selected_grade_level_ids ) or self.env['school_base.grade_level'] options['selected_grade_level_ids'] = selected_grade_levels.mapped( 'name') options['homeroom'] = previous_options and previous_options.get( 'homeroom') or '' options['selected_homeroom'] = options['homeroom'] @api.model def _get_options_account_type(self, options): ''' Get select account type in the filter widget (see filter_account_type). :param options: The report options. :return: Selected account types. ''' all_account_types = [] account_types = [] for account_type_option in options.get('account_type', []): if account_type_option['selected']: account_types.append(account_type_option) all_account_types.append(account_type_option) return account_types or all_account_types @api.model def _get_options_domain(self, options): # OVERRIDE # Handle filter_unreconciled + filter_account_type domain = super(AccountStudentLedger, self)._get_options_domain(options) if options.get('unreconciled'): domain.append(('full_reconcile_id', '=', False)) domain.append( ('account_id.internal_type', 'in', [t['id'] for t in self._get_options_account_type(options)])) return domain @api.model def _get_options_partner_domain(self, options): domain = [] if options.get('partner_ids'): student_ids = [int(student) for student in options['partner_ids']] domain.append(('student_id', 'in', student_ids)) if options.get('family_ids'): family_ids = [int(family) for family in options['family_ids']] domain.append(('family_id', 'in', family_ids)) if options.get('grade_level_ids'): grade_level_ids = [ int(grade_level) for grade_level in options['grade_level_ids'] ] domain.append(('student_id.grade_level_id', 'in', grade_level_ids)) if options.get('partner_categories'): partner_category_ids = [ int(category) for category in options['partner_categories'] ] domain.append( ('student_id.category_id', 'in', partner_category_ids)) if options.get('homeroom'): domain.append(('homeroom', '=ilike', options['homeroom'])) return domain @api.model def _get_options_sum_balance(self, options): ''' Create options with the 'strict_range' enabled on the filter_date. The resulting dates domain will be: [ ('date' <= options['date_to']), ('date' >= options['date_from']) ] :param options: The report options. :return: A copy of the options. ''' new_options = options.copy() new_options['date'] = new_options['date'].copy() new_options['date']['strict_range'] = True return new_options @api.model def _get_options_initial_balance(self, options): ''' Create options used to compute the initial balances for each partner. The resulting dates domain will be: [('date' <= options['date_from'] - 1)] :param options: The report options. :return: A copy of the options. ''' new_options = options.copy() new_options['date'] = new_options['date'].copy() new_date_to = fields.Date.from_string( new_options['date']['date_from']) - timedelta(days=1) new_options['date'].update({ 'date_from': False, 'date_to': fields.Date.to_string(new_date_to), }) return new_options def _set_context(self, options): ctx = super(AccountStudentLedger, self)._set_context(options) if options.get('family_ids'): ctx['family_ids'] = self.env['res.partner'].browse( [int(family) for family in options['family_ids']]) if options.get('grade_level_ids'): ctx['grade_level_ids'] = self.env[ 'school_base.grade_level'].browse([ int(grade_level) for grade_level in options['grade_level_ids'] ]) if options.get('homeroom'): ctx['homeroom'] = options['homeroom'] return ctx def get_report_informations(self, options): info = super(AccountStudentLedger, self).get_report_informations(options) options = options or self._get_options(options) if options.get('family'): info['options']['selected_family_ids'] = [ self.env['res.partner'].browse(int(family)).name for family in options['family_ids'] ] if options.get('grade_level'): info['options']['selected_grade_level_ids'] = [ self.env['school_base.grade_level'].browse( int(grade_level)).name for grade_level in options['grade_level_ids'] ] if options.get('homeroom'): info['options']['selected_grade_level_ids'] = options['homeroom'] return info #################################################### # QUERIES #################################################### @api.model def _get_query_sums(self, options, expanded_partner=None): ''' Construct a query retrieving all the aggregated sums to build the report. It includes: - sums for all accounts. - sums for the initial balances. - sums for the unaffected earnings. - sums for the tax declaration. :param options: The report options. :param expanded_partner: An optional account.account record that must be specified when expanding a line with of without the load more. :return: (query, params) ''' params = [] queries = [] if expanded_partner: if expanded_partner.id == self.env.company.partner_id.id: domain = [('student_id', '=', False)] else: domain = [('student_id', '=', expanded_partner.id)] else: domain = [] # Create the currency table. ct_query = self._get_query_currency_table(options) # Get sums for all partners. # period: [('date' <= options['date_to']), ('date' >= options['date_from'])] new_options = self._get_options_sum_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) params += where_params queries.append(''' SELECT account_move_line.student_id AS groupby, 'sum' AS key, SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit, SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit, SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance FROM %s LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id WHERE %s GROUP BY account_move_line.student_id ''' % (tables, ct_query, where_clause)) # Get sums for the initial balance. # period: [('date' <= options['date_from'] - 1)] new_options = self._get_options_initial_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) params += where_params queries.append(''' SELECT account_move_line.student_id AS groupby, 'initial_balance' AS key, SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit, SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit, SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance FROM %s LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id WHERE %s GROUP BY account_move_line.student_id ''' % (tables, ct_query, where_clause)) return ' UNION ALL '.join(queries), params @api.model def _get_query_amls(self, options, expanded_partner=None, offset=None, limit=None): ''' Construct a query retrieving the account.move.lines when expanding a report line with or without the load more. :param options: The report options. :param expanded_partner: The res.partner record corresponding to the expanded line. :param offset: The offset of the query (used by the load more). :param limit: The limit of the query (used by the load more). :return: (query, params) ''' unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) # Get sums for the account move lines. # period: [('date' <= options['date_to']), ('date', '>=', options['date_from'])] if expanded_partner: if expanded_partner.id == self.env.company.partner_id.id: domain = [('student_id', '=', False)] else: domain = [('student_id', '=', expanded_partner.id)] elif unfold_all: domain = [] elif options['unfolded_lines']: domain = [('student_id', 'in', [int(line[8:]) for line in options['unfolded_lines']])] new_options = self._get_options_sum_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) ct_query = self._get_query_currency_table(options) query = ''' SELECT account_move_line.id, account_move_line.date, account_move_line.date_maturity, account_move_line.name, account_move_line.ref, account_move_line.company_id, account_move_line.account_id, account_move_line.payment_id, account_move_line.student_id, account_move_line.currency_id, account_move_line.amount_currency, ROUND(account_move_line.debit * currency_table.rate, currency_table.precision) AS debit, ROUND(account_move_line.credit * currency_table.rate, currency_table.precision) AS credit, ROUND(account_move_line.balance * currency_table.rate, currency_table.precision) AS balance, account_move_line__move_id.name AS move_name, company.currency_id AS company_currency_id, partner.name AS partner_name, account_move_line__move_id.type AS move_type, account.code AS account_code, account.name AS account_name, journal.code AS journal_code, journal.name AS journal_name, full_rec.name AS full_rec_name FROM account_move_line LEFT JOIN account_move account_move_line__move_id ON account_move_line__move_id.id = account_move_line.move_id LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id LEFT JOIN res_company company ON company.id = account_move_line.company_id LEFT JOIN res_partner partner ON partner.id = account_move_line.student_id LEFT JOIN account_account account ON account.id = account_move_line.account_id LEFT JOIN account_journal journal ON journal.id = account_move_line.journal_id LEFT JOIN account_full_reconcile full_rec ON full_rec.id = account_move_line.full_reconcile_id WHERE %s ORDER BY account_move_line.id ''' % (ct_query, where_clause) if offset: query += ' OFFSET %s ' where_params.append(offset) if limit: query += ' LIMIT %s ' where_params.append(limit) return query, where_params @api.model def _do_query(self, options, expanded_partner=None): ''' Execute the queries, perform all the computation and return partners_results, a lists of tuple (partner, fetched_values) sorted by the table's model _order: - partner is a res.parter record. - fetched_values is a dictionary containing: - sum: {'debit': float, 'credit': float, 'balance': float} - (optional) initial_balance: {'debit': float, 'credit': float, 'balance': float} - (optional) lines: [line_vals_1, line_vals_2, ...] :param options: The report options. :param expanded_account: An optional account.account record that must be specified when expanding a line with of without the load more. :param fetch_lines: A flag to fetch the account.move.lines or not (the 'lines' key in accounts_values). :return: (accounts_values, taxes_results) ''' company_currency = self.env.company.currency_id # Execute the queries and dispatch the results. query, params = self._get_query_sums(options, expanded_partner=expanded_partner) groupby_partners = {} self._cr.execute(query, params) for res in self._cr.dictfetchall(): key = res['key'] if key == 'sum': if not company_currency.is_zero( res['debit']) or not company_currency.is_zero( res['credit']): groupby_partners.setdefault(res['groupby'], {}) groupby_partners[res['groupby']][key] = res elif key == 'initial_balance': if not company_currency.is_zero(res['balance']): groupby_partners.setdefault(res['groupby'], {}) groupby_partners[res['groupby']][key] = res # Fetch the lines of unfolded accounts. unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) if expanded_partner or unfold_all or options['unfolded_lines']: query, params = self._get_query_amls( options, expanded_partner=expanded_partner) self._cr.execute(query, params) for res in self._cr.dictfetchall(): if res['student_id'] not in groupby_partners: continue groupby_partners[res['student_id']].setdefault('lines', []) groupby_partners[res['student_id']]['lines'].append(res) # Retrieve the partners to browse. # groupby_partners.keys() contains all account ids affected by: # - the amls in the current period. # - the amls affecting the initial balance. # Note a search is done instead of a browse to preserve the table ordering. if groupby_partners.get(None): groupby_partners[ self.env.company.partner_id.id] = groupby_partners.pop(None) if expanded_partner: partners = expanded_partner elif groupby_partners: partners = self.env['res.partner'].with_context( active_test=False).search([('id', 'in', list(groupby_partners.keys()))]) else: partners = [] return [(partner, groupby_partners[partner.id]) for partner in partners] #################################################### # COLUMNS/LINES #################################################### @api.model def _get_report_line_partner(self, options, partner, initial_balance, debit, credit, balance): company_currency = self.env.company.currency_id unfold_all = self._context.get( 'print_mode') and not options.get('unfolded_lines') columns = [ { 'name': self.format_value(initial_balance), 'class': 'number' }, { 'name': self.format_value(debit), 'class': 'number' }, { 'name': self.format_value(credit), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': ''}) columns.append({'name': self.format_value(balance), 'class': 'number'}) return { 'id': 'partner_%s' % partner.id, 'name': 'Undefined' if partner.id == self.env.company.partner_id.id else partner.name[:128], 'columns': columns, 'level': 2, 'trust': partner.trust, 'unfoldable': not company_currency.is_zero(debit) or not company_currency.is_zero(credit), 'unfolded': 'partner_%s' % partner.id in options['unfolded_lines'] or unfold_all, 'colspan': 6, } @api.model def _get_report_line_move_line(self, options, partner, aml, cumulated_init_balance, cumulated_balance): if aml['payment_id']: caret_type = 'account.payment' elif aml['move_type'] in ('in_refund', 'in_invoice', 'in_receipt'): caret_type = 'account.invoice.in' elif aml['move_type'] in ('out_refund', 'out_invoice', 'out_receipt'): caret_type = 'account.invoice.out' else: caret_type = 'account.move' date_maturity = aml['date_maturity'] and format_date( self.env, fields.Date.from_string(aml['date_maturity'])) columns = [ { 'name': aml['journal_code'] }, { 'name': aml['account_code'] }, { 'name': self._format_aml_name(aml['name'], aml['ref'], aml['move_name']) }, { 'name': date_maturity or '', 'class': 'date' }, { 'name': aml['full_rec_name'] or '' }, { 'name': self.format_value(cumulated_init_balance), 'class': 'number' }, { 'name': self.format_value(aml['debit'], blank_if_zero=True), 'class': 'number' }, { 'name': self.format_value(aml['credit'], blank_if_zero=True), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): if aml['currency_id']: currency = self.env['res.currency'].browse(aml['currency_id']) formatted_amount = self.format_value(aml['amount_currency'], currency=currency, blank_if_zero=True) columns.append({'name': formatted_amount, 'class': 'number'}) else: columns.append({'name': ''}) columns.append({ 'name': self.format_value(cumulated_balance), 'class': 'number' }) return { 'id': aml['id'], 'parent_id': 'partner_%s' % partner.id, 'name': format_date(self.env, aml['date']), 'class': 'date', 'columns': columns, 'caret_options': caret_type, 'level': 4, } @api.model def _get_report_line_load_more(self, options, partner, offset, remaining, progress): return { 'id': 'loadmore_%s' % partner.id, 'offset': offset, 'progress': progress, 'remaining': remaining, 'class': 'o_account_reports_load_more text-center', 'parent_id': 'account_%s' % partner.id, 'name': _('Load more... (%s remaining)' % remaining), 'colspan': 10 if self.user_has_groups('base.group_multi_currency') else 9, 'columns': [{}], } @api.model def _get_report_line_total(self, options, initial_balance, debit, credit, balance): columns = [ { 'name': self.format_value(initial_balance), 'class': 'number' }, { 'name': self.format_value(debit), 'class': 'number' }, { 'name': self.format_value(credit), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': ''}) columns.append({'name': self.format_value(balance), 'class': 'number'}) return { 'id': 'partner_ledger_total_%s' % self.env.company.id, 'name': _('Total'), 'class': 'total', 'level': 1, 'columns': columns, 'colspan': 6, } @api.model def _get_partner_ledger_lines(self, options, line_id=None): ''' Get lines for the whole report or for a specific line. :param options: The report options. :return: A list of lines, each one represented by a dictionary. ''' lines = [] unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) expanded_partner = line_id and self.env['res.partner'].browse( int(line_id[8:])) partners_results = self._do_query(options, expanded_partner=expanded_partner) total_initial_balance = total_debit = total_credit = total_balance = 0.0 for partner, results in partners_results: is_unfolded = 'partner_%s' % partner.id in options[ 'unfolded_lines'] # res.partner record line. partner_sum = results.get('sum', {}) partner_init_bal = results.get('initial_balance', {}) initial_balance = partner_init_bal.get('balance', 0.0) debit = partner_sum.get('debit', 0.0) credit = partner_sum.get('credit', 0.0) balance = initial_balance + partner_sum.get('balance', 0.0) lines.append( self._get_report_line_partner(options, partner, initial_balance, debit, credit, balance)) total_initial_balance += initial_balance total_debit += debit total_credit += credit total_balance += balance if unfold_all or is_unfolded: cumulated_balance = initial_balance # account.move.line record lines. amls = results.get('lines', []) load_more_remaining = len(amls) load_more_counter = self._context.get( 'print_mode') and load_more_remaining or self.MAX_LINES for aml in amls: # Don't show more line than load_more_counter. if load_more_counter == 0: break cumulated_init_balance = cumulated_balance cumulated_balance += aml['balance'] lines.append( self._get_report_line_move_line( options, partner, aml, cumulated_init_balance, cumulated_balance)) load_more_remaining -= 1 load_more_counter -= 1 if load_more_remaining > 0: # Load more line. lines.append( self._get_report_line_load_more( options, partner, self.MAX_LINES, load_more_remaining, cumulated_balance, )) if not line_id: # Report total line. lines.append( self._get_report_line_total(options, total_initial_balance, total_debit, total_credit, total_balance)) return lines @api.model def _load_more_lines(self, options, line_id, offset, load_more_remaining, progress): ''' Get lines for an expanded line using the load more. :param options: The report options. :return: A list of lines, each one represented by a dictionary. ''' lines = [] expanded_partner = line_id and self.env['res.partner'].browse( int(line_id[9:])) load_more_counter = self.MAX_LINES # Fetch the next batch of lines. amls_query, amls_params = self._get_query_amls( options, expanded_partner=expanded_partner, offset=offset, limit=load_more_counter) self._cr.execute(amls_query, amls_params) for aml in self._cr.dictfetchall(): # Don't show more line than load_more_counter. if load_more_counter == 0: break cumulated_init_balance = progress progress += aml['balance'] # account.move.line record line. lines.append( self._get_report_line_move_line(options, expanded_partner, aml, cumulated_init_balance, progress)) offset += 1 load_more_remaining -= 1 load_more_counter -= 1 if load_more_remaining > 0: # Load more line. lines.append( self._get_report_line_load_more( options, expanded_partner, offset, load_more_remaining, progress, )) return lines def _get_columns_name(self, options): columns = [{}, { 'name': _('JRNL') }, { 'name': _('Account') }, { 'name': _('Ref') }, { 'name': _('Due Date'), 'class': 'date' }, { 'name': _('Matching Number') }, { 'name': _('Initial Balance'), 'class': 'number' }, { 'name': _('Debit'), 'class': 'number' }, { 'name': _('Credit'), 'class': 'number' }] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': _('Amount Currency'), 'class': 'number'}) columns.append({'name': _('Balance'), 'class': 'number'}) return columns @api.model def _get_lines(self, options, line_id=None): offset = int(options.get('lines_offset', 0)) remaining = int(options.get('lines_remaining', 0)) balance_progress = float(options.get('lines_progress', 0)) if offset > 0: # Case a line is expanded using the load more. res = self._load_more_lines(options, line_id, offset, remaining, balance_progress) return res else: # Case the whole report is loaded or a line is expanded for the first time. res = self._get_partner_ledger_lines(options, line_id=line_id) return res @api.model def _get_report_name(self): return _('Student Ledger')
'sum'] else sql_results[0]['sum'] net_new_mrr = new_mrr - churned_mrr + expansion_mrr - down_mrr return { 'new_mrr': new_mrr, 'churned_mrr': -churned_mrr, 'expansion_mrr': expansion_mrr, 'down_mrr': -down_mrr, 'net_new_mrr': net_new_mrr, } STAT_TYPES = { 'mrr': { 'name': _lt('Monthly Recurring Revenue'), 'code': 'mrr', 'dir': 'up', 'prior': 1, 'type': 'last', 'add_symbol': 'currency', 'compute': compute_mrr }, 'net_revenue': { 'name': _lt('Net Revenue'), 'code': 'net_revenue', 'dir': 'up', 'prior': 2, 'type': 'sum', 'add_symbol': 'currency', 'compute': compute_net_revenue
def _get_profitability_labels(self): labels = super()._get_profitability_labels() labels['manufacturing_order'] = _lt('Manufacturing Orders') return labels
currency_fields = [ k for k, v in fields if v.type == 'many2one' and v.comodel_name == 'res.currency' ] if currency_fields: options['display_currency'] = record[currency_fields[0]] if 'date' not in options: options['date'] = record._context.get('date') if 'company_id' not in options: options['company_id'] = record._context.get('company_id') return super(MonetaryConverter, self).record_to_html(record, field_name, options) TIMEDELTA_UNITS = (('year', _lt('year'), 3600 * 24 * 365), ('month', _lt('month'), 3600 * 24 * 30), ('week', _lt('week'), 3600 * 24 * 7), ('day', _lt('day'), 3600 * 24), ('hour', _lt('hour'), 3600), ('minute', _lt('minute'), 60), ('second', _lt('second'), 1)) class FloatTimeConverter(models.AbstractModel): """ ``float_time`` converter, to display integral or fractional values as human-readable time spans (e.g. 1.5 as "01:30"). Can be used on any numerical field. """ _name = 'ir.qweb.field.float_time' _description = 'Qweb Field Float Time' _inherit = 'ir.qweb.field'
class IntrastatReport(models.AbstractModel): _name = 'account.intrastat.report' _description = 'Intrastat Report' _inherit = 'account.report' filter_date = {'mode': 'range', 'filter': 'this_month'} filter_journals = True filter_multi_company = None filter_with_vat = False filter_intrastat_type = [ { 'name': _lt('Arrival'), 'selected': False, 'id': 'arrival' }, { 'name': _lt('Dispatch'), 'selected': False, 'id': 'dispatch' }, ] filter_intrastat_extended = True def _get_filter_journals(self): #only show sale/purchase journals return self.env['account.journal'].search( [('company_id', 'in', self.env.companies.ids or [self.env.company.id]), ('type', 'in', ('sale', 'purchase'))], order="company_id, name") def _get_columns_name(self, options): columns = [ { 'name': '' }, { 'name': _('Date') }, { 'name': _('System') }, { 'name': _('Country Code') }, { 'name': _('Transaction Code') }, { 'name': _('Region Code') }, { 'name': _('Commodity Code') }, { 'name': _('Type') }, { 'name': _('Origin Country') }, { 'name': _('Partner VAT') }, ] if options.get('intrastat_extended'): columns += [ { 'name': _('Transport Code') }, { 'name': _('Incoterm Code') }, ] columns += [ { 'name': _('Weight') }, { 'name': _('Quantity') }, { 'name': _('Value'), 'class': 'number' }, ] return columns @api.model def _create_intrastat_report_line(self, options, vals): caret_options = 'account.invoice.%s' % ( vals['invoice_type'] in ('in_invoice', 'in_refund') and 'in' or 'out') columns = [{ 'name': c } for c in [ vals['invoice_date'], vals['system'], vals['country_code'], vals['trans_code'], vals['region_code'], vals['commodity_code'], vals['type'], vals['intrastat_product_origin_country'], vals['partner_vat'], ]] if options.get('intrastat_extended'): columns += [{ 'name': c } for c in [ vals['invoice_transport'] or vals['company_transport'] or '', vals['invoice_incoterm'] or vals['company_incoterm'] or '', ]] columns += [{ 'name': c } for c in [ vals['weight'], vals['quantity'], self.format_value(vals['value']) ]] return { 'id': vals['id'], 'caret_options': caret_options, 'model': 'account.move.line', 'name': vals['invoice_number'], 'columns': columns, 'level': 2, } @api.model def _decode_options(self, options): journal_ids = self.env['account.journal'].search([ ('type', 'in', ('sale', 'purchase')) ]).ids if options.get('journals'): journal_ids = [ c['id'] for c in options['journals'] if c.get('selected') ] or journal_ids if options.get('intrastat_type'): incl_arrivals = options['intrastat_type'][0]['selected'] incl_dispatches = options['intrastat_type'][1]['selected'] if not incl_arrivals and not incl_dispatches: incl_arrivals = incl_dispatches = True else: incl_arrivals = incl_dispatches = True return options['date']['date_from'], options['date']['date_to'], journal_ids, \ incl_arrivals, incl_dispatches, options.get('intrastat_extended'), options.get('with_vat') @api.model def _prepare_query(self, date_from, date_to, journal_ids, invoice_types=None, with_vat=False): query_blocks, params = self._build_query(date_from, date_to, journal_ids, invoice_types=invoice_types, with_vat=with_vat) query = 'SELECT %(select)s FROM %(from)s WHERE %(where)s ORDER BY %(order)s' % query_blocks return query, params @api.model def _build_query(self, date_from, date_to, journal_ids, invoice_types=None, with_vat=False): # triangular use cases are handled by letting the intrastat_country_id editable on # invoices. Modifying or emptying it allow to alter the intrastat declaration # accordingly to specs (https://www.nbb.be/doc/dq/f_pdf_ex/intra2017fr.pdf (§ 4.x)) select = ''' row_number() over () AS sequence, CASE WHEN inv.move_type IN ('in_invoice', 'out_refund') THEN %(import_merchandise_code)s ELSE %(export_merchandise_code)s END AS system, country.code AS country_code, company_country.code AS comp_country_code, CASE WHEN inv_line.intrastat_transaction_id IS NULL THEN '1' ELSE transaction.code END AS transaction_code, company_region.code AS region_code, code.code AS commodity_code, inv_line.id AS id, prodt.id AS template_id, inv.id AS invoice_id, inv.currency_id AS invoice_currency_id, inv.name AS invoice_number, coalesce(inv.date, inv.invoice_date) AS invoice_date, inv.move_type AS invoice_type, inv_incoterm.code AS invoice_incoterm, comp_incoterm.code AS company_incoterm, inv_transport.code AS invoice_transport, comp_transport.code AS company_transport, CASE WHEN inv_line.intrastat_transaction_id IS NULL THEN '1' ELSE transaction.code END AS trans_code, CASE WHEN inv.move_type IN ('in_invoice', 'out_refund') THEN 'Arrival' ELSE 'Dispatch' END AS type, prod.weight * inv_line.quantity * ( CASE WHEN inv_line_uom.category_id IS NULL OR inv_line_uom.category_id = prod_uom.category_id THEN 1 ELSE inv_line_uom.factor END ) AS weight, inv_line.quantity * ( CASE WHEN inv_line_uom.category_id IS NULL OR inv_line_uom.category_id = prod_uom.category_id THEN 1 ELSE inv_line_uom.factor END ) AS quantity, inv_line.price_subtotal AS value, CASE WHEN inv_line.intrastat_product_origin_country_id IS NULL THEN \'QU\' -- If you don't know the country of origin of the goods, as an exception you may replace the country code by "QU". ELSE product_country.code END AS intrastat_product_origin_country, CASE WHEN partner_country.id IS NULL THEN \'QV999999999999\' -- For VAT numbers of companies outside the European Union, for example in the case of triangular trade, you always have to use the code "QV999999999999". ELSE partner.vat END AS partner_vat ''' from_ = ''' account_move_line inv_line LEFT JOIN account_move inv ON inv_line.move_id = inv.id LEFT JOIN account_intrastat_code transaction ON inv_line.intrastat_transaction_id = transaction.id LEFT JOIN res_company company ON inv.company_id = company.id LEFT JOIN account_intrastat_code company_region ON company.intrastat_region_id = company_region.id LEFT JOIN res_partner partner ON inv_line.partner_id = partner.id LEFT JOIN res_partner comp_partner ON company.partner_id = comp_partner.id LEFT JOIN res_country country ON inv.intrastat_country_id = country.id LEFT JOIN res_country company_country ON comp_partner.country_id = company_country.id INNER JOIN product_product prod ON inv_line.product_id = prod.id LEFT JOIN product_template prodt ON prod.product_tmpl_id = prodt.id LEFT JOIN account_intrastat_code code ON prodt.intrastat_id = code.id LEFT JOIN uom_uom inv_line_uom ON inv_line.product_uom_id = inv_line_uom.id LEFT JOIN uom_uom prod_uom ON prodt.uom_id = prod_uom.id LEFT JOIN account_incoterms inv_incoterm ON inv.invoice_incoterm_id = inv_incoterm.id LEFT JOIN account_incoterms comp_incoterm ON company.incoterm_id = comp_incoterm.id LEFT JOIN account_intrastat_code inv_transport ON inv.intrastat_transport_mode_id = inv_transport.id LEFT JOIN account_intrastat_code comp_transport ON company.intrastat_transport_mode_id = comp_transport.id LEFT JOIN res_country product_country ON product_country.id = inv_line.intrastat_product_origin_country_id LEFT JOIN res_country partner_country ON partner.country_id = partner_country.id AND partner_country.intrastat IS TRUE ''' where = ''' inv.state = 'posted' AND inv_line.display_type IS NULL AND NOT inv_line.quantity = 0 AND inv.company_id = %(company_id)s AND company_country.id != country.id AND country.intrastat = TRUE AND coalesce(inv.date, inv.invoice_date) >= %(date_from)s AND coalesce(inv.date, inv.invoice_date) <= %(date_to)s AND prodt.type != 'service' AND inv.journal_id IN %(journal_ids)s AND inv.move_type IN %(invoice_types)s AND NOT inv_line.exclude_from_invoice_tab ''' order = 'inv.invoice_date DESC' params = { 'company_id': self.env.company.id, 'import_merchandise_code': _merchandise_import_code.get(self.env.company.country_id.code, '29'), 'export_merchandise_code': _merchandise_export_code.get(self.env.company.country_id.code, '19'), 'date_from': date_from, 'date_to': date_to, 'journal_ids': tuple(journal_ids), } if with_vat: where += ' AND partner.vat IS NOT NULL ' if invoice_types: params['invoice_types'] = tuple(invoice_types) else: params['invoice_types'] = ('out_invoice', 'out_refund', 'in_invoice', 'in_refund') query = { 'select': select, 'from': from_, 'where': where, 'order': order, } return query, params @api.model def _fill_missing_values(self, vals, cache=None): ''' Some values are too complex to be retrieved in the SQL query. Then, this method is used to compute the missing values fetched from the database. :param vals: A dictionary created by the dictfetchall method. :param cache: A cache dictionary used to avoid performance loss. ''' if cache is None: cache = {} # Prefetch data before looping self.env['product.template'].browse([v['template_id'] for v in vals]).read( ['intrastat_id', 'categ_id']) self.env['product.category'].search([]).read( ['intrastat_id', 'parent_id']) for index in range(len(vals)): # Check account.intrastat.code # If missing, retrieve the commodity code by looking in the product category recursively. if not vals[index]['commodity_code']: cache_key = 'commodity_code_%d' % vals[index]['template_id'] if cache_key not in cache: product = self.env['product.template'].browse( vals[index]['template_id']) intrastat_code = product.search_intrastat_code() cache[cache_key] = vals[index][ 'commodity_code'] = intrastat_code.code vals[index]['commodity_code'] = cache.get(cache_key) # Check the currency. cache_key = 'currency_%d' % vals[index]['invoice_currency_id'] if cache_key not in cache: cache[cache_key] = self.env['res.currency'].browse( vals[index]['invoice_currency_id']) company_currency_id = self.env.company.currency_id if cache[cache_key] != company_currency_id: vals[index]['value'] = cache[cache_key]._convert( vals[index]['value'], company_currency_id, self.env.company, vals[index]['invoice_date']) return vals @api.model def _get_lines(self, options, line_id=None): self.env['account.move.line'].check_access_rights('read') date_from, date_to, journal_ids, incl_arrivals, incl_dispatches, extended, with_vat = self._decode_options( options) invoice_types = [] if incl_arrivals: invoice_types += ['in_invoice', 'out_refund'] if incl_dispatches: invoice_types += ['out_invoice', 'in_refund'] query, params = self._prepare_query(date_from, date_to, journal_ids, invoice_types=invoice_types, with_vat=with_vat) self._cr.execute(query, params) query_res = self._cr.dictfetchall() # Create lines lines = [] total_value = 0 for vals in self._fill_missing_values(query_res): line = self._create_intrastat_report_line(options, vals) lines.append(line) total_value += vals['value'] # Create total line if only one type selected. if incl_arrivals != incl_dispatches: colspan = 12 if extended else 10 lines.append({ 'id': 0, 'name': _('Total'), 'class': 'total', 'level': 2, 'columns': [{ 'name': v } for v in [self.format_value(total_value)]], 'colspan': colspan, }) return lines @api.model def _get_report_name(self): return _('Intrastat Report')
class ReportPartnerLedger(models.AbstractModel): _inherit = "account.report" _name = "account.partner.ledger" _description = "Partner Ledger" filter_date = {'mode': 'range', 'filter': 'this_year'} filter_all_entries = False filter_unfold_all = False filter_account_type = [ { 'id': 'receivable', 'name': _lt('Receivable'), 'selected': False }, { 'id': 'payable', 'name': _lt('Payable'), 'selected': False }, ] filter_unreconciled = False filter_partner = True @api.model def _get_templates(self): templates = super(ReportPartnerLedger, self)._get_templates() templates[ 'line_template'] = 'account_reports.line_template_partner_ledger_report' templates[ 'main_template'] = 'account_reports.main_template_with_filter_input_partner' return templates #################################################### # OPTIONS #################################################### @api.model def _get_options_account_type(self, options): ''' Get select account type in the filter widget (see filter_account_type). :param options: The report options. :return: Selected account types. ''' all_account_types = [] account_types = [] for account_type_option in options.get('account_type', []): if account_type_option['selected']: account_types.append(account_type_option) all_account_types.append(account_type_option) return account_types or all_account_types @api.model def _get_options_domain(self, options): # OVERRIDE # Handle filter_unreconciled + filter_account_type domain = super(ReportPartnerLedger, self)._get_options_domain(options) if options.get('unreconciled'): domain.append(('full_reconcile_id', '=', False)) if self.env.context.get('model') == 'account.partner.ledger': domain += [ '!', '&', '&', '&', ('credit', '=', 0.0), ('debit', '=', 0.0), ('amount_currency', '!=', 0.0), ('ref', 'ilike', 'EXCH%') ] domain.append( ('account_id.internal_type', 'in', [t['id'] for t in self._get_options_account_type(options)])) return domain @api.model def _get_options_sum_balance(self, options): ''' Create options with the 'strict_range' enabled on the filter_date. The resulting dates domain will be: [ ('date' <= options['date_to']), ('date' >= options['date_from']) ] :param options: The report options. :return: A copy of the options. ''' new_options = options.copy() new_options['date'] = new_options['date'].copy() new_options['date']['strict_range'] = True return new_options @api.model def _get_options_initial_balance(self, options): ''' Create options used to compute the initial balances for each partner. The resulting dates domain will be: [('date' <= options['date_from'] - 1)] :param options: The report options. :return: A copy of the options. ''' new_options = options.copy() new_options['date'] = new_options['date'].copy() new_date_to = fields.Date.from_string( new_options['date']['date_from']) - timedelta(days=1) new_options['date'].update({ 'date_from': False, 'date_to': fields.Date.to_string(new_date_to), }) return new_options @api.model def _get_options_without_partner(self, options): ''' Create options used to compute the special case of lines without partner reconcile with another line having a partner for each partner. :param options: The report options. :return: A copy of the options. ''' new_options = options.copy() new_options['date'] = new_options['date'].copy() new_options['date'].update({ 'date_from': False, }) return new_options #################################################### # QUERIES #################################################### @api.model def _get_query_sums(self, options, expanded_partner=None): ''' Construct a query retrieving all the aggregated sums to build the report. It includes: - sums for all partners. - sums for the initial balances. :param options: The report options. :param expanded_partner: An optional res.partner record that must be specified when expanding a line with of without the load more. :return: (query, params) ''' params = [] queries = [] if expanded_partner is not None: domain = [('partner_id', '=', expanded_partner.id)] else: domain = [] # Create the currency table. ct_query = self.env['res.currency']._get_query_currency_table(options) # Get sums for all partners. # period: [('date' <= options['date_to']), ('date' >= options['date_from'])] new_options = self._get_options_sum_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) params += where_params queries.append(''' SELECT account_move_line.partner_id AS groupby, 'sum' AS key, SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit, SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit, SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance FROM %s LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id WHERE %s GROUP BY account_move_line.partner_id ''' % (tables, ct_query, where_clause)) # Get sums for the initial balance. # period: [('date' <= options['date_from'] - 1)] new_options = self._get_options_initial_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) params += where_params queries.append(''' SELECT account_move_line.partner_id AS groupby, 'initial_balance' AS key, SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit, SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit, SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance FROM %s LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id WHERE %s GROUP BY account_move_line.partner_id ''' % (tables, ct_query, where_clause)) return ' UNION ALL '.join(queries), params @api.model def _get_lines_without_partner(self, options, expanded_partner=None, offset=0, limit=0): ''' Get the detail of lines without partner reconciled with a line with a partner. Those lines should be considered as belonging the partner for the reconciled amount as it may clear some of the partner invoice/bill and they have to be accounted in the partner balance.''' params = [] if expanded_partner: partner_clause = '= %s' params = [expanded_partner.id] + params else: partner_clause = 'IS NOT NULL' new_options = self._get_options_without_partner(options) params += [options['date']['date_from'], options['date']['date_to']] tables, where_clause, where_params = self._query_get(new_options, domain=[]) params += where_params + [offset] limit_clause = '' if limit != 0: params += [limit] limit_clause = "LIMIT %s" query = ''' SELECT account_move_line.id, account_move_line.date, account_move_line.date_maturity, account_move_line.name, account_move_line.ref, account_move_line.company_id, account_move_line.account_id, account_move_line.payment_id, aml_with_partner.partner_id, account_move_line.currency_id, account_move_line.amount_currency, account_move_line.matching_number, CASE WHEN aml_with_partner.balance > 0 THEN 0 ELSE partial.amount END AS debit, CASE WHEN aml_with_partner.balance < 0 THEN 0 ELSE partial.amount END AS credit, CASE WHEN aml_with_partner.balance > 0 THEN -partial.amount ELSE partial.amount END AS balance, account_move_line__move_id.name AS move_name, account_move_line__move_id.move_type AS move_type, account.code AS account_code, account.name AS account_name, journal.code AS journal_code, journal.name AS journal_name, full_rec.name AS full_rec_name FROM {tables}, account_partial_reconcile partial LEFT JOIN account_full_reconcile full_rec ON full_rec.id = partial.full_reconcile_id, account_move_line aml_with_partner, account_journal journal, account_account account WHERE (account_move_line.id = partial.debit_move_id OR account_move_line.id = partial.credit_move_id) AND account_move_line.partner_id IS NULL AND (aml_with_partner.id = partial.debit_move_id OR aml_with_partner.id = partial.credit_move_id) AND aml_with_partner.partner_id {partner_clause} AND journal.id = account_move_line.journal_id AND account.id = account_move_line.account_id AND partial.max_date BETWEEN %s AND %s AND {where_clause} ORDER BY account_move_line.date, account_move_line.id OFFSET %s {limit_clause} '''.format(tables=tables, partner_clause=partner_clause, where_clause=where_clause, limit_clause=limit_clause) return query, params @api.model def _get_sums_without_partner(self, options, expanded_partner=None): ''' Get the sum of lines without partner reconciled with a line with a partner, grouped by partner. Those lines should be considered as belonging the partner for the reconciled amount as it may clear some of the partner invoice/bill and they have to be accounted in the partner balance.''' params = [] if expanded_partner: partner_clause = '= %s' params = [expanded_partner.id] else: partner_clause = 'IS NOT NULL' new_options = self._get_options_without_partner(options) params = [options['date']['date_from'] ] + params + [options['date']['date_to']] tables, where_clause, where_params = self._query_get(new_options, domain=[]) params += where_params query = ''' SELECT aml_with_partner.partner_id AS groupby, SUM(CASE WHEN aml_with_partner.balance > 0 THEN 0 ELSE partial.amount END) AS debit, SUM(CASE WHEN aml_with_partner.balance < 0 THEN 0 ELSE partial.amount END) AS credit, SUM(CASE WHEN aml_with_partner.balance > 0 THEN -partial.amount ELSE partial.amount END) AS balance, CASE WHEN partial.max_date < %s THEN 'initial_balance' ELSE 'sum' END as key FROM {tables}, account_partial_reconcile partial, account_move_line aml_with_partner WHERE (account_move_line.id = partial.debit_move_id OR account_move_line.id = partial.credit_move_id) AND account_move_line.partner_id IS NULL AND (aml_with_partner.id = partial.debit_move_id OR aml_with_partner.id = partial.credit_move_id) AND aml_with_partner.partner_id {partner_clause} AND partial.max_date <= %s AND {where_clause} GROUP BY aml_with_partner.partner_id, key '''.format(tables=tables, partner_clause=partner_clause, where_clause=where_clause) return query, params @api.model def _get_query_amls(self, options, expanded_partner=None, offset=None, limit=None): ''' Construct a query retrieving the account.move.lines when expanding a report line with or without the load more. :param options: The report options. :param expanded_partner: The res.partner record corresponding to the expanded line. :param offset: The offset of the query (used by the load more). :param limit: The limit of the query (used by the load more). :return: (query, params) ''' unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) # Get sums for the account move lines. # period: [('date' <= options['date_to']), ('date', '>=', options['date_from'])] if expanded_partner is not None: domain = [('partner_id', '=', expanded_partner.id)] elif unfold_all: domain = [] elif options['unfolded_lines']: domain = [('partner_id', 'in', [int(line[8:]) for line in options['unfolded_lines']])] new_options = self._get_options_sum_balance(options) tables, where_clause, where_params = self._query_get(new_options, domain=domain) ct_query = self.env['res.currency']._get_query_currency_table(options) query = ''' SELECT account_move_line.id, account_move_line.date, account_move_line.date_maturity, account_move_line.name, account_move_line.ref, account_move_line.company_id, account_move_line.account_id, account_move_line.payment_id, account_move_line.partner_id, account_move_line.currency_id, account_move_line.amount_currency, account_move_line.matching_number, ROUND(account_move_line.debit * currency_table.rate, currency_table.precision) AS debit, ROUND(account_move_line.credit * currency_table.rate, currency_table.precision) AS credit, ROUND(account_move_line.balance * currency_table.rate, currency_table.precision) AS balance, account_move_line__move_id.name AS move_name, company.currency_id AS company_currency_id, partner.name AS partner_name, account_move_line__move_id.move_type AS move_type, account.code AS account_code, account.name AS account_name, journal.code AS journal_code, journal.name AS journal_name FROM account_move_line LEFT JOIN account_move account_move_line__move_id ON account_move_line__move_id.id = account_move_line.move_id LEFT JOIN %s ON currency_table.company_id = account_move_line.company_id LEFT JOIN res_company company ON company.id = account_move_line.company_id LEFT JOIN res_partner partner ON partner.id = account_move_line.partner_id LEFT JOIN account_account account ON account.id = account_move_line.account_id LEFT JOIN account_journal journal ON journal.id = account_move_line.journal_id WHERE %s ORDER BY account_move_line.date, account_move_line.id ''' % (ct_query, where_clause) if offset: query += ' OFFSET %s ' where_params.append(offset) if limit: query += ' LIMIT %s ' where_params.append(limit) return query, where_params @api.model def _do_query(self, options, expanded_partner=None): ''' Execute the queries, perform all the computation and return partners_results, a lists of tuple (partner, fetched_values) sorted by the table's model _order: - partner is a res.parter record. - fetched_values is a dictionary containing: - sum: {'debit': float, 'credit': float, 'balance': float} - (optional) initial_balance: {'debit': float, 'credit': float, 'balance': float} - (optional) lines: [line_vals_1, line_vals_2, ...] :param options: The report options. :param expanded_account: An optional account.account record that must be specified when expanding a line with of without the load more. :param fetch_lines: A flag to fetch the account.move.lines or not (the 'lines' key in accounts_values). :return: (accounts_values, taxes_results) ''' def assign_sum(row): key = row['key'] fields = ['balance', 'debit', 'credit' ] if key == 'sum' else ['balance'] if any(not company_currency.is_zero(row[field]) for field in fields): groupby_partners.setdefault( row['groupby'], defaultdict(lambda: defaultdict(float))) for field in fields: groupby_partners[row['groupby']][key][field] += row[field] company_currency = self.env.company.currency_id # flush the tables that gonna be queried self.env['account.move.line'].flush( fnames=self.env['account.move.line']._fields) self.env['account.move'].flush(fnames=self.env['account.move']._fields) self.env['account.partial.reconcile'].flush( fnames=self.env['account.partial.reconcile']._fields) # Execute the queries and dispatch the results. query, params = self._get_query_sums(options, expanded_partner=expanded_partner) groupby_partners = {} self._cr.execute(query, params) for res in self._cr.dictfetchall(): assign_sum(res) # Fetch the lines of unfolded accounts. unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) if expanded_partner or unfold_all or options['unfolded_lines']: query, params = self._get_query_amls( options, expanded_partner=expanded_partner) self._cr.execute(query, params) for res in self._cr.dictfetchall(): if res['partner_id'] not in groupby_partners: continue groupby_partners[res['partner_id']].setdefault('lines', []) groupby_partners[res['partner_id']]['lines'].append(res) query, params = self._get_lines_without_partner( options, expanded_partner=expanded_partner) self._cr.execute(query, params) for row in self._cr.dictfetchall(): # don't show lines of partners not expanded if row['partner_id'] in groupby_partners: groupby_partners[row['partner_id']].setdefault('lines', []) row['class'] = ' text-muted' groupby_partners[row['partner_id']]['lines'].append(row) if None in groupby_partners: # reconciled lines without partners are fetched to be displayed under the matched partner # and thus but be inversed to be displayed under the unknown partner none_row = row.copy() none_row['class'] = ' text-muted' none_row['debit'] = row['credit'] none_row['credit'] = row['debit'] none_row['balance'] = -row['balance'] groupby_partners[None].setdefault('lines', []) groupby_partners[None]['lines'].append(none_row) # correct the sums per partner, for the lines without partner reconciled with a line having a partner query, params = self._get_sums_without_partner( options, expanded_partner=expanded_partner) self._cr.execute(query, params) total = total_debit = total_credit = total_initial_balance = 0 for row in self._cr.dictfetchall(): key = row['key'] total_debit += key == 'sum' and row['debit'] or 0 total_credit += key == 'sum' and row['credit'] or 0 total_initial_balance += key == 'initial_balance' and row[ 'balance'] or 0 total += key == 'sum' and row['balance'] or 0 if None not in groupby_partners and not ( expanded_partner or unfold_all or options['unfolded_lines']): groupby_partners.setdefault(None, {}) if row['groupby'] not in groupby_partners: continue assign_sum(row) if None in groupby_partners: if 'sum' not in groupby_partners[None]: groupby_partners[None].setdefault('sum', { 'debit': 0, 'credit': 0, 'balance': 0 }) if 'initial_balance' not in groupby_partners[None]: groupby_partners[None].setdefault('initial_balance', {'balance': 0}) #debit/credit are inversed for the unknown partner as the computation is made regarding the balance of the known partner groupby_partners[None]['sum']['debit'] += total_credit groupby_partners[None]['sum']['credit'] += total_debit groupby_partners[None]['sum']['balance'] -= total groupby_partners[None]['initial_balance'][ 'balance'] -= total_initial_balance # Retrieve the partners to browse. # groupby_partners.keys() contains all account ids affected by: # - the amls in the current period. # - the amls affecting the initial balance. # Note a search is done instead of a browse to preserve the table ordering. if expanded_partner: partners = expanded_partner elif groupby_partners: partners = self.env['res.partner'].with_context( active_test=False).search([('id', 'in', list(groupby_partners.keys()))]) else: partners = [] # Add 'Partner Unknown' if needed if None in groupby_partners.keys(): partners = [p for p in partners] + [None] return [(partner, groupby_partners[partner.id if partner else None]) for partner in partners] #################################################### # COLUMNS/LINES #################################################### @api.model def _get_report_line_partner(self, options, partner, initial_balance, debit, credit, balance): company_currency = self.env.company.currency_id unfold_all = self._context.get( 'print_mode') and not options.get('unfolded_lines') columns = [ { 'name': self.format_value(initial_balance), 'class': 'number' }, { 'name': self.format_value(debit), 'class': 'number' }, { 'name': self.format_value(credit), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': ''}) columns.append({'name': self.format_value(balance), 'class': 'number'}) return { 'id': 'partner_%s' % (partner.id if partner else 0), 'partner_id': partner.id if partner else None, 'name': partner is not None and (partner.name or '')[:128] or _('Unknown Partner'), 'columns': columns, 'level': 2, 'trust': partner.trust if partner else None, 'unfoldable': not company_currency.is_zero(debit) or not company_currency.is_zero(credit), 'unfolded': 'partner_%s' % (partner.id if partner else 0) in options['unfolded_lines'] or unfold_all, 'colspan': 6, } @api.model def _get_report_line_move_line(self, options, partner, aml, cumulated_init_balance, cumulated_balance): if aml['payment_id']: caret_type = 'account.payment' else: caret_type = 'account.move' date_maturity = aml['date_maturity'] and format_date( self.env, fields.Date.from_string(aml['date_maturity'])) columns = [ { 'name': aml['journal_code'] }, { 'name': aml['account_code'] }, { 'name': self._format_aml_name(aml['name'], aml['ref'], aml['move_name']) }, { 'name': date_maturity or '', 'class': 'date' }, { 'name': aml['matching_number'] or '' }, { 'name': self.format_value(cumulated_init_balance), 'class': 'number' }, { 'name': self.format_value(aml['debit'], blank_if_zero=True), 'class': 'number' }, { 'name': self.format_value(aml['credit'], blank_if_zero=True), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): if aml['currency_id']: currency = self.env['res.currency'].browse(aml['currency_id']) formatted_amount = self.format_value(aml['amount_currency'], currency=currency, blank_if_zero=True) columns.append({'name': formatted_amount, 'class': 'number'}) else: columns.append({'name': ''}) columns.append({ 'name': self.format_value(cumulated_balance), 'class': 'number' }) return { 'id': aml['id'], 'parent_id': 'partner_%s' % (partner.id if partner else 0), 'name': format_date(self.env, aml['date']), 'class': 'text' + aml.get('class', ''), # do not format as date to prevent text centering 'columns': columns, 'caret_options': caret_type, 'level': 2, } @api.model def _get_report_line_load_more(self, options, partner, offset, remaining, progress): return { 'id': 'loadmore_%s' % (partner.id if partner else 0), 'offset': offset, 'progress': progress, 'remaining': remaining, 'class': 'o_account_reports_load_more text-center', 'parent_id': 'partner_%s' % (partner.id if partner else 0), 'name': _('Load more... (%s remaining)', remaining), 'colspan': 10 if self.user_has_groups('base.group_multi_currency') else 9, 'columns': [{}], } @api.model def _get_report_line_total(self, options, initial_balance, debit, credit, balance): columns = [ { 'name': self.format_value(initial_balance), 'class': 'number' }, { 'name': self.format_value(debit), 'class': 'number' }, { 'name': self.format_value(credit), 'class': 'number' }, ] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': ''}) columns.append({'name': self.format_value(balance), 'class': 'number'}) return { 'id': 'partner_ledger_total_%s' % self.env.company.id, 'name': _('Total'), 'class': 'total', 'level': 1, 'columns': columns, 'colspan': 6, } @api.model def _get_partner_ledger_lines(self, options, line_id=None): ''' Get lines for the whole report or for a specific line. :param options: The report options. :return: A list of lines, each one represented by a dictionary. ''' lines = [] unfold_all = options.get('unfold_all') or ( self._context.get('print_mode') and not options['unfolded_lines']) expanded_partner = line_id and self.env['res.partner'].browse( int(line_id[8:])) partners_results = self._do_query(options, expanded_partner=expanded_partner) total_initial_balance = total_debit = total_credit = total_balance = 0.0 for partner, results in partners_results: is_unfolded = 'partner_%s' % (partner.id if partner else 0) in options['unfolded_lines'] # res.partner record line. partner_sum = results.get('sum', {}) partner_init_bal = results.get('initial_balance', {}) initial_balance = partner_init_bal.get('balance', 0.0) debit = partner_sum.get('debit', 0.0) credit = partner_sum.get('credit', 0.0) balance = initial_balance + partner_sum.get('balance', 0.0) lines.append( self._get_report_line_partner(options, partner, initial_balance, debit, credit, balance)) total_initial_balance += initial_balance total_debit += debit total_credit += credit total_balance += balance if unfold_all or is_unfolded: cumulated_balance = initial_balance # account.move.line record lines. amls = results.get('lines', []) load_more_remaining = len(amls) load_more_counter = self._context.get( 'print_mode') and load_more_remaining or self.MAX_LINES for aml in amls: # Don't show more line than load_more_counter. if load_more_counter == 0: break cumulated_init_balance = cumulated_balance cumulated_balance += aml['balance'] lines.append( self._get_report_line_move_line( options, partner, aml, cumulated_init_balance, cumulated_balance)) load_more_remaining -= 1 load_more_counter -= 1 if load_more_remaining > 0: # Load more line. lines.append( self._get_report_line_load_more( options, partner, self.MAX_LINES, load_more_remaining, cumulated_balance, )) if not line_id: # Report total line. lines.append( self._get_report_line_total(options, total_initial_balance, total_debit, total_credit, total_balance)) return lines @api.model def _load_more_lines(self, options, line_id, offset, load_more_remaining, progress): ''' Get lines for an expanded line using the load more. :param options: The report options. :return: A list of lines, each one represented by a dictionary. ''' lines = [] expanded_partner = line_id and self.env['res.partner'].browse( int(line_id[9:])) load_more_counter = self.MAX_LINES starting_offset = offset starting_load_more_counter = load_more_counter # Fetch the next batch of lines amls_query, amls_params = self._get_query_amls( options, expanded_partner=expanded_partner, offset=offset, limit=load_more_counter) self._cr.execute(amls_query, amls_params) for aml in self._cr.dictfetchall(): # Don't show more line than load_more_counter. if load_more_counter == 0: break cumulated_init_balance = progress progress += aml['balance'] # account.move.line record line. lines.append( self._get_report_line_move_line(options, expanded_partner, aml, cumulated_init_balance, progress)) offset += 1 load_more_remaining -= 1 load_more_counter -= 1 query, params = self._get_lines_without_partner( options, expanded_partner=expanded_partner, offset=offset - starting_offset, limit=starting_load_more_counter - load_more_counter) self._cr.execute(query, params) for row in self._cr.dictfetchall(): # Don't show more line than load_more_counter. if load_more_counter == 0: break row['class'] = ' text-muted' if line_id == 'loadmore_0': # reconciled lines without partners are fetched to be displayed under the matched partner # and thus but be inversed to be displayed under the unknown partner row['debit'] = row['balance'] < 0 and row['credit'] or 0 row['credit'] = row['balance'] > 0 and row['debit'] or 0 row['balance'] = -row['balance'] cumulated_init_balance = progress progress += row['balance'] lines.append( self._get_report_line_move_line(options, expanded_partner, row, cumulated_init_balance, progress)) offset += 1 load_more_remaining -= 1 load_more_counter -= 1 if load_more_remaining > 0: # Load more line. lines.append( self._get_report_line_load_more( options, expanded_partner, offset, load_more_remaining, progress, )) return lines def _get_columns_name(self, options): columns = [{}, { 'name': _('JRNL') }, { 'name': _('Account') }, { 'name': _('Ref') }, { 'name': _('Due Date'), 'class': 'date' }, { 'name': _('Matching Number') }, { 'name': _('Initial Balance'), 'class': 'number' }, { 'name': _('Debit'), 'class': 'number' }, { 'name': _('Credit'), 'class': 'number' }] if self.user_has_groups('base.group_multi_currency'): columns.append({'name': _('Amount Currency'), 'class': 'number'}) columns.append({'name': _('Balance'), 'class': 'number'}) return columns @api.model def _get_lines(self, options, line_id=None): offset = int(options.get('lines_offset', 0)) remaining = int(options.get('lines_remaining', 0)) balance_progress = float(options.get('lines_progress', 0)) if offset > 0: # Case a line is expanded using the load more. return self._load_more_lines(options, line_id, offset, remaining, balance_progress) else: # Case the whole report is loaded or a line is expanded for the first time. return self._get_partner_ledger_lines(options, line_id=line_id) @api.model def _get_report_name(self): return _('Partner Ledger')
from odoo.tools import pdf import io import PIL.PdfImagePlugin # activate PDF support in PIL from PIL import Image from odoo.addons.delivery_ups.models.ups_request import UPSRequest from odoo.http import request from zeep.exceptions import Fault import base64 _logger = logging.getLogger(__name__) import re ZIP_ZIP4 = re.compile('^[0-9]{5}(-[0-9]{4})?$') UPS_ERROR_MAP = { '110002': _lt("Please provide at least one item to ship."), '110208': _lt("Please set a valid country in the recipient address."), '110308': _lt("Please set a valid country in the warehouse address."), '110548': _lt("A shipment cannot have a KGS/IN or LBS/CM as its unit of measurements. Configure it from the delivery method."), '111057': _lt("This measurement system is not valid for the selected country. Please switch from LBS/IN to KGS/CM (or vice versa). Configure it from the delivery method."), '111091': _lt("The selected service is not possible from your warehouse to the recipient address, please choose another service."), '111100': _lt("The selected service is invalid from the requested warehouse, please choose another service."), '111107': _lt("Please provide a valid zip code in the warehouse address."), '111210': _lt("The selected service is invalid to the recipient address, please choose another service."), '111212': _lt("Please provide a valid package type available for service and selected locations."), '111500': _lt("The selected service is not valid with the selected packaging."), '112111': _lt("Please provide a valid shipper number/Carrier Account."), '113020': _lt("Please provide a valid zip code in the warehouse address."), '113021': _lt("Please provide a valid zip code in the recipient address."), '120031': _lt("Exceeds Total Number of allowed pieces per World Wide Express Shipment."), '120100': _lt("Please provide a valid shipper number/Carrier Account."),
def _get_profitability_labels(self): labels = super()._get_profitability_labels() labels['expenses'] = _lt('Expenses') return labels
from datetime import datetime from dateutil.relativedelta import relativedelta import io, base64 # months in (int, str) tuples MONTHS = [(str(month), '%02d' % month) for month in list(range(1, 13))] TIMEZONE_RELATIVEDELTA = relativedelta(hours=7) STATES = [('draft', 'Draft'), ('allocation_derived', 'Allocation Derived'), ('posted', 'Posted')] DEFAULT_STATE = STATES[0][0] MODEL_NAMES = { 'material_loss': _lt('Material Loss Allocation'), 'labor_cost': _lt('Labor Cost Allocation'), 'click_charge': _lt('Click Charge Allocation'), 'overhead_cost': _lt('Overhead Cost Allocation'), } SPECIFIC_FIELDS = { 'material_loss': { 'je_inverse_field': 'material_loss_allocation_id' }, 'labor_cost': { 'je_inverse_field': 'labor_cost_allocation_id' }, 'click_charge': { 'je_inverse_field': 'click_charge_allocation_id' },
def _get_profitability_labels(self): labels = super()._get_profitability_labels() labels['purchase_order'] = _lt('Purchase Orders') return labels
class ReportPartnerLedger141(models.AbstractModel): _inherit = "account.partner.ledger" _name = "account.partner.ledger.141" filter_account_type = [ { 'id': 'receivable', 'name': _lt('Receivable'), 'selected': False }, { 'id': 'payable', 'name': _lt('Payable'), 'selected': False }, { 'id': 'other', 'name': _lt('Regular'), 'selected': True }, { 'id': 'liquidity', 'name': _lt('Liquidity'), 'selected': False }, ] @api.model def _get_options_domain(self, options): # OVERRIDE # Handle filter_unreconciled + filter_account_type domain = super(ReportPartnerLedger141, self)._get_options_domain(options) i = 0 for item in domain: i += 1 if 'account_id.internal_type' in item: domain[i - 1] = ('account_id.internal_type', 'in', [ 'receivable', 'payable', 'other', 'liquidity' ]) # if 'partner_id' in item: # domain[i - 1] = ('|','partner_id', '!=', False,'partner_id', '=', False) domain.append(('account_id.code', '=', '141')) # Partner must be set. return domain @api.model def _get_templates(self): templates = super(ReportPartnerLedger141, self)._get_templates() templates[ 'line_template'] = 'advanced_vn_report.line_template_partner_ledger_report_141' return templates @api.model def _get_report_line_move_line(self, options, partner, aml, cumulated_init_balance, cumulated_balance): result = super(ReportPartnerLedger141, self)._get_report_line_move_line( options, partner, aml, cumulated_init_balance, cumulated_balance) result['account_id'] = 141 return result
# -*- coding: utf-8 -*- import babel.dates import pytz from lxml import etree import base64 import json from odoo import _, _lt, api, fields, models from odoo.osv.expression import AND, TRUE_DOMAIN, normalize_domain from odoo.tools import date_utils, lazy from odoo.tools.misc import get_lang from odoo.exceptions import UserError from collections import defaultdict SEARCH_PANEL_ERROR_MESSAGE = _lt("Too many items to display.") def is_true_domain(domain): return normalize_domain(domain) == TRUE_DOMAIN class lazymapping(defaultdict): def __missing__(self, key): value = self.default_factory(key) self[key] = value return value class IrActionsActWindowView(models.Model): _inherit = 'ir.actions.act_window.view'
# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import logging import requests from odoo import api, fields, models, _, _lt from odoo.exceptions import UserError _logger = logging.getLogger(__name__) TWITTER_EXCEPTION = { 304: _lt('There was no new data to return.'), 400: _lt('The request was invalid or cannot be otherwise served. Requests without authentication are considered invalid and will yield this response.' ), 401: _lt('Authentication credentials were missing or incorrect. Maybe screen name tweets are protected.' ), 403: _lt('The request is understood, but it has been refused or access is not allowed. Please check your Twitter API Key and Secret.' ), 429: _lt('Request cannot be served due to the applications rate limit having been exhausted for the resource.' ), 500: _lt('Twitter seems broken. Please retry later. You may consider posting an issue on Twitter forums to get help.' ), 502:
net_new_mrr = new_mrr - churned_mrr + expansion_mrr - down_mrr return { 'new_mrr': new_mrr, 'churned_mrr': -churned_mrr, 'expansion_mrr': expansion_mrr, 'down_mrr': -down_mrr, 'net_new_mrr': net_new_mrr, } STAT_TYPES = { 'mrr': { 'name': _lt('Monthly Recurring Revenue'), 'code': 'mrr', 'tooltip': _lt('MRR for short; total subscription revenue per month (e.g. for an annual subscription of $ 1,200, the MRR is $ 100)' ), 'dir': 'up', 'prior': 1, 'type': 'last', 'add_symbol': 'currency', 'compute': compute_mrr