def test_build_4_functionfield01(self): name = 'get_pretty_properties' funfield = function_field_registry.get(FakeContact, name) self.assertIsNotNone(funfield) cell = EntityCellFunctionField(model=FakeContact, func_field=funfield) self.assertIsInstance(cell, EntityCellFunctionField) self.assertEqual(name, cell.value) self.assertEqual(str(funfield.verbose_name), cell.title) self.assertEqual('function_field-{}'.format(funfield.name), cell.key) # self.assertIs(cell.has_a_filter, True) # self.assertIs(cell.editable, False) # self.assertIs(cell.sortable, False) self.assertIs(cell.is_hidden, False) self.assertIs(cell.is_multiline, True) # self.assertEqual('', cell.filter_string) self.assertEqual(settings.CSS_DEFAULT_LISTVIEW, cell.listview_css_class) self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class) cell = EntityCellFunctionField.build(FakeContact, func_field_name=name) self.assertIsInstance(cell, EntityCellFunctionField) self.assertEqual(name, cell.value) self.assertIsNone( EntityCellFunctionField.build(FakeContact, func_field_name='invalid'))
def test_functionfield01(self): self.assertEqual(_('Computed fields'), EntityCellFunctionField.verbose_name) name = 'get_pretty_properties' funfield = function_field_registry.get(FakeContact, name) self.assertIsNotNone(funfield) cell = EntityCellFunctionField(model=FakeContact, func_field=funfield) self.assertIsInstance(cell, EntityCellFunctionField) self.assertEqual(name, cell.value) self.assertEqual(str(funfield.verbose_name), cell.title) self.assertEqual(f'function_field-{funfield.name}', cell.key) self.assertIs(cell.is_hidden, False) self.assertIs(cell.is_multiline, True) self.assertEqual(settings.CSS_DEFAULT_LISTVIEW, cell.listview_css_class) self.assertEqual(settings.CSS_DEFAULT_HEADER_LISTVIEW, cell.header_listview_css_class) cell = EntityCellFunctionField.build(FakeContact, func_field_name=name) self.assertIsInstance(cell, EntityCellFunctionField) self.assertEqual(name, cell.value) self.assertIsNone( EntityCellFunctionField.build(FakeContact, func_field_name='invalid'))
def _get_cells(self, model): cells = [] for cell_cls, data in self._cells: cell = None if cell_cls is EntityCellRegularField: cell = EntityCellRegularField.build(model=model, name=data) elif cell_cls is EntityCellFunctionField: cell = EntityCellFunctionField.build(model, func_field_name=data) else: # EntityCellRelation rtype = self._get_relationtype(data) if rtype is False: logger.warning( 'SmartColumnsRegistry: relation type "%s" does not exist', data) else: cell = EntityCellRelation(model=model, rtype=rtype) # Has no sense here: # EntityCellActions : not configurable in HeaderFilter form # EntityCellCustomField : dynamically created by user # TODO: other types if cell is not None: cells.append(cell) return cells
def test_relation_brick01(self): rtype = RelationType.create( ('test-subject_loves', 'loves'), ('test-object_loved', 'is loved by'), )[0] rbi = RelationBrickItem.objects.create_if_needed(rtype.id) self.assertIsInstance(rbi, RelationBrickItem) self.assertIsNotNone(rbi.pk) self.assertEqual(rtype.id, rbi.relation_type_id) get_ct = ContentType.objects.get_for_model ct_contact = get_ct(FakeContact) ct_orga = get_ct(FakeOrganisation) ct_img = get_ct(FakeImage) rbi = self.refresh(rbi) # Test persistence self.assertIsNone(rbi.get_cells(ct_contact)) self.assertIsNone(rbi.get_cells(ct_orga)) self.assertIsNone(rbi.get_cells(ct_img)) self.assertIs(rbi.all_ctypes_configured, False) rbi.set_cells( ct_contact, [ EntityCellRegularField.build(FakeContact, 'last_name'), EntityCellFunctionField.build(FakeContact, 'get_pretty_properties'), ], ) rbi.set_cells(ct_orga, [EntityCellRegularField.build(FakeOrganisation, 'name')]) rbi.save() rbi = self.refresh(rbi) # Test persistence self.assertIsNone(rbi.get_cells(ct_img)) self.assertIs(rbi.all_ctypes_configured, False) cells_contact = rbi.get_cells(ct_contact) self.assertEqual(2, len(cells_contact)) cell_contact = cells_contact[0] self.assertIsInstance(cell_contact, EntityCellRegularField) self.assertEqual('last_name', cell_contact.value) cell_contact = cells_contact[1] self.assertIsInstance(cell_contact, EntityCellFunctionField) self.assertEqual('get_pretty_properties', cell_contact.value) self.assertEqual(1, len(rbi.get_cells(ct_orga))) # --- self.assertEqual(rbi, RelationBrickItem.objects.create_if_needed(rtype.id))
def _build_hf_n_contacts(self): user = self.user create_orga = partial(FakeOrganisation.objects.create, user=user) self.organisations = organisations = { name: create_orga(name=name) for name in ('Bebop', 'Swordfish') } rtype_pilots = RelationType.create(('test-subject_pilots', 'pilots'), ('test-object_pilots', 'is piloted by') )[0] create_ptype = CremePropertyType.create ptype_beautiful = create_ptype(str_pk='test-prop_beautiful', text='is beautiful') ptype_girl = create_ptype(str_pk='test-prop_girl', text='is a girl') create_contact = partial(FakeContact.objects.create, user=user) self.contacts = contacts = { first_name: create_contact(first_name=first_name, last_name=last_name) for first_name, last_name in [('Spike', 'Spiegel'), ('Jet', 'Black'), ('Faye', 'Valentine'), ('Edward', 'Wong'), ] } create_rel = partial(Relation.objects.create, user=user, type=rtype_pilots, object_entity=organisations['Bebop'], ) create_rel(subject_entity=contacts['Jet']) create_rel(subject_entity=contacts['Spike']) create_rel(subject_entity=contacts['Spike'], object_entity=organisations['Swordfish']) create_prop = CremeProperty.objects.create create_prop(type=ptype_girl, creme_entity=contacts['Faye']) create_prop(type=ptype_girl, creme_entity=contacts['Edward']) create_prop(type=ptype_beautiful, creme_entity=contacts['Faye']) cells = [ EntityCellRegularField.build(model=FakeContact, name='civility'), EntityCellRegularField.build(model=FakeContact, name='last_name'), EntityCellRegularField.build(model=FakeContact, name='first_name'), EntityCellRelation(model=FakeContact, rtype=rtype_pilots), # TODO: EntityCellCustomField EntityCellFunctionField.build(model=FakeContact, func_field_name='get_pretty_properties'), ] hf = HeaderFilter.create(pk='test-hf_contact', name='Contact view', model=FakeContact, cells_desc=cells, ) # return cells return hf
def test_search_invalid_cell_type(self): "No error." self.login() ct = ContentType.objects.get_for_model(FakeOrganisation) cell = EntityCellFunctionField.build(FakeOrganisation, 'get_pretty_properties') self.assertIsNotNone(cell) SearchConfigItem.objects.create(content_type=ct, cells=[cell]) response = self._search('cool', ct.id) self.assertEqual(200, response.status_code)
def test_average01(self): "Regular field." agg = RGAAverage( cell=EntityCellRegularField.build(FakeOrganisation, 'capital')) self.assertIsNone(agg.error) # --- with self.assertRaises(ValueError) as cm1: RGAAverage(cell=None) self.assertEqual(_('the field does not exist any more.'), str(cm1.exception)) # --- with self.assertRaises(ValueError) as cm2: RGAAverage(cell=EntityCellFunctionField.build( FakeOrganisation, 'get_pretty_properties'), ) self.assertIn('invalid type of cell', str(cm2.exception))
def test_functionfields(self): field = EntityCellsField(model=FakeContact) name1 = 'get_pretty_properties' value = f'function_field-{name1}' self.assertCellInChoices( value, choices=self._find_sub_widget(field, 'function_field').choices, ) self.assertListEqual( [EntityCellFunctionField.build(FakeContact, name1)], field.clean(value)) name2 = 'invalid' self.assertFieldValidationError( UniformEntityCellsField, 'invalid_value', field.clean, f'function_field-{name2}', message_args={'value': name2}, )
def test_functionfield01(self): "Function field are not sortable by default." sorter = QuerySorter() field_name = 'name' cells = [ EntityCellRegularField.build(model=FakeOrganisation, name=field_name), EntityCellFunctionField.build( model=FakeContact, func_field_name='get_pretty_properties'), ] sortinfo = sorter.get( model=FakeInvoice, cells=cells, cell_key=cells[1].key, order=Order(), ) self.assertEqual((field_name, 'cremeentity_ptr_id'), sortinfo.field_names) self.assertEqual(cells[0].key, sortinfo.main_cell_key) self.assertTrue(sortinfo.main_order.asc)
def _create_report(self, name='Report #1', efilter=None, extra_cells=()): cells = [ EntityCellRegularField.build(model=FakeContact, name='last_name'), EntityCellRegularField.build(model=FakeContact, name='user'), EntityCellRelation(model=FakeContact, rtype=RelationType.objects.get(pk=REL_SUB_HAS)), EntityCellFunctionField.build(model=FakeContact, func_field_name='get_pretty_properties'), *extra_cells, ] hf = HeaderFilter.create(pk='test_hf', name='name', model=FakeContact, cells_desc=cells) response = self.client.post(self.ADD_URL, follow=True, data={'user': self.user.pk, 'name': name, 'ct': self.ct_contact.id, 'hf': hf.id, 'filter': efilter.id if efilter else '', }, ) self.assertNoFormError(response) return self.get_object_or_fail(Report, name=name)
def test_copy02(self): "Attribute <_sub_fields> (container)." field1 = EntityCellsField(model=FakeContact) field2 = deepcopy(field1) registry = EntityCellsRegistry() registry(EntityCellRegularField) registry(EntityCellRelation) field1.cell_registry = registry ffield_name = 'get_pretty_properties' value = f'function_field-{ffield_name}' self.assertFieldValidationError( EntityCellsField, 'invalid_type', field1.clean, value, message_args={'type_id': 'function_field'}, ) self.assertListEqual( [EntityCellFunctionField.build(FakeContact, ffield_name)], field2.clean(value))
def populate(self): already_populated = RelationType.objects.filter( pk=constants.REL_SUB_BILL_ISSUED, ).exists() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() # Relationships --------------------------- line_entities = [*lines_registry] create_rtype = RelationType.create create_rtype( (constants.REL_SUB_BILL_ISSUED, _('issued by'), BILLING_MODELS), (constants.REL_OBJ_BILL_ISSUED, _('has issued'), [Organisation]), is_internal=True, minimal_display=(False, True), ) rt_sub_bill_received = create_rtype( (constants.REL_SUB_BILL_RECEIVED, _('received by'), BILLING_MODELS), (constants.REL_OBJ_BILL_RECEIVED, _('has received'), [Organisation, Contact]), is_internal=True, minimal_display=(False, True), )[0] create_rtype( (constants.REL_SUB_HAS_LINE, _('had the line'), BILLING_MODELS), (constants.REL_OBJ_HAS_LINE, _('is the line of'), line_entities), is_internal=True, minimal_display=(True, True), ) create_rtype( (constants.REL_SUB_LINE_RELATED_ITEM, _('has the related item'), line_entities), (constants.REL_OBJ_LINE_RELATED_ITEM, _('is the related item of'), [Product, Service]), is_internal=True, ) create_rtype( ( constants.REL_SUB_CREDIT_NOTE_APPLIED, _('is used in the billing document'), [CreditNote], ), ( constants.REL_OBJ_CREDIT_NOTE_APPLIED, _('uses the credit note'), [Quote, SalesOrder, Invoice], ), is_internal=True, minimal_display=(True, True), ) if apps.is_installed('creme.activities'): logger.info( 'Activities app is installed ' '=> an Invoice/Quote/SalesOrder can be the subject of an Activity' ) from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT RelationType.objects.get( pk=REL_SUB_ACTIVITY_SUBJECT, ).add_subject_ctypes( Invoice, Quote, SalesOrder) # Payment Terms --------------------------- create_if_needed( PaymentTerms, {'pk': 1}, name=_('Deposit'), description=_(r'20% deposit will be required'), is_custom=False, ) # SalesOrder Status --------------------------- def create_order_status(pk, name, **kwargs): create_if_needed(SalesOrderStatus, {'pk': pk}, name=name, **kwargs) # NB: pk=1 + is_custom=False --> default status # (used when a quote is converted in invoice for example) create_order_status(1, pgettext('billing-salesorder', 'Issued'), order=1, is_custom=False) if not already_populated: create_order_status(2, pgettext('billing-salesorder', 'Accepted'), order=3) create_order_status(3, pgettext('billing-salesorder', 'Rejected'), order=4) create_order_status(4, pgettext('billing-salesorder', 'Created'), order=2) # Invoice Status --------------------------- def create_invoice_status(pk, name, **kwargs): create_if_needed(InvoiceStatus, {'pk': pk}, name=name, **kwargs) create_invoice_status( 1, pgettext('billing-invoice', 'Draft'), order=1, is_custom=False, ) # Default status create_invoice_status( 2, pgettext('billing-invoice', 'To be sent'), order=2, is_custom=False, ) if not already_populated: create_invoice_status( 3, pgettext('billing-invoice', 'Sent'), order=3, pending_payment=True, ) create_invoice_status( 4, pgettext('billing-invoice', 'Resulted'), order=5, ) create_invoice_status( 5, pgettext('billing-invoice', 'Partly resulted'), order=4, pending_payment=True, ) create_invoice_status( 6, _('Collection'), order=7, ) create_invoice_status( 7, _('Resulted collection'), order=6, ) create_invoice_status( 8, pgettext('billing-invoice', 'Canceled'), order=8, ) # CreditNote Status --------------------------- def create_cnote_status(pk, name, **kwargs): create_if_needed(CreditNoteStatus, {'pk': pk}, name=name, **kwargs) create_cnote_status(1, pgettext('billing-creditnote', 'Draft'), order=1, is_custom=False) if not already_populated: create_cnote_status(2, pgettext('billing-creditnote', 'Issued'), order=2) create_cnote_status(3, pgettext('billing-creditnote', 'Consumed'), order=3) create_cnote_status(4, pgettext('billing-creditnote', 'Out of date'), order=4) # --------------------------- EntityFilter.objects.smart_update_or_create( 'billing-invoices_unpaid', name=_('Invoices unpaid'), model=Invoice, user='******', conditions=[ condition_handler.RegularFieldConditionHandler.build_condition( model=Invoice, operator=operators.EqualsOperator, field_name='status__pending_payment', values=[True], ), ], ) EntityFilter.objects.smart_update_or_create( 'billing-invoices_unpaid_late', name=_('Invoices unpaid and late'), model=Invoice, user='******', conditions=[ condition_handler.RegularFieldConditionHandler.build_condition( model=Invoice, operator=operators.EqualsOperator, field_name='status__pending_payment', values=[True], ), condition_handler.DateRegularFieldConditionHandler. build_condition( model=Invoice, field_name='expiration_date', date_range='in_past', ), ], ) current_year_invoice_filter = EntityFilter.objects.smart_update_or_create( 'billing-current_year_invoices', name=_('Current year invoices'), model=Invoice, user='******', conditions=[ condition_handler.DateRegularFieldConditionHandler. build_condition( model=Invoice, field_name='issuing_date', date_range='current_year', ), ], ) current_year_unpaid_invoice_filter = EntityFilter.objects.smart_update_or_create( 'billing-current_year_unpaid_invoices', name=_('Current year and unpaid invoices'), model=Invoice, user='******', conditions=[ condition_handler.DateRegularFieldConditionHandler. build_condition( model=Invoice, field_name='issuing_date', date_range='current_year', ), condition_handler.RegularFieldConditionHandler.build_condition( model=Invoice, operator=operators.EqualsOperator, field_name='status__pending_payment', values=[True], ), ], ) # --------------------------- def create_hf(hf_pk, name, model, status=True): HeaderFilter.objects.create_if_needed( pk=hf_pk, name=name, model=model, cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), EntityCellRelation(model=model, rtype=rt_sub_bill_received), (EntityCellRegularField, { 'name': 'number' }), (EntityCellRegularField, { 'name': 'status' }) if status else None, (EntityCellRegularField, { 'name': 'total_no_vat' }), (EntityCellRegularField, { 'name': 'issuing_date' }), (EntityCellRegularField, { 'name': 'expiration_date' }), ], ) create_hf(constants.DEFAULT_HFILTER_INVOICE, _('Invoice view'), Invoice) create_hf(constants.DEFAULT_HFILTER_QUOTE, _('Quote view'), Quote) create_hf(constants.DEFAULT_HFILTER_ORDER, _('Sales order view'), SalesOrder) create_hf(constants.DEFAULT_HFILTER_CNOTE, _('Credit note view'), CreditNote) create_hf( constants.DEFAULT_HFILTER_TEMPLATE, _('Template view'), TemplateBase, status=False, ) def create_hf_lines(hf_pk, name, model): build_cell = EntityCellRegularField.build HeaderFilter.objects.create_if_needed( pk=hf_pk, name=name, model=model, cells_desc=[ build_cell(model=model, name='on_the_fly_item'), build_cell(model=model, name='quantity'), build_cell(model=model, name='unit_price'), ], ) create_hf_lines('billing-hg_product_lines', _('Product lines view'), ProductLine) create_hf_lines('billing-hg_service_lines', _('Service lines view'), ServiceLine) # --------------------------- creation_only_groups_desc = [ { 'name': _('Properties'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.CREME_PROPERTIES }, ), ], }, { 'name': _('Relationships'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.RELATIONS }, ), ], }, ] def build_custom_form_items(model, creation_descriptor, edition_descriptor, field_names): base_groups = [ { 'name': _('General information'), 'layout': LAYOUT_DUAL_FIRST, 'cells': [ *((EntityCellRegularField, { 'name': fname }) for fname in field_names), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_REGULARFIELDS }, ), ], }, { 'name': _('Organisations'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ BillingSourceSubCell(model=model).into_cell(), BillingTargetSubCell(model=model).into_cell(), ], }, { 'name': _('Description'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ (EntityCellRegularField, { 'name': 'description' }), ], }, { 'name': _('Custom fields'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_CUSTOMFIELDS }, ), ], }, ] CustomFormConfigItem.objects.create_if_needed( descriptor=creation_descriptor, groups_desc=[ *base_groups, *creation_only_groups_desc, ], ) CustomFormConfigItem.objects.create_if_needed( descriptor=edition_descriptor, groups_desc=base_groups, ) build_custom_form_items( model=Invoice, creation_descriptor=custom_forms.INVOICE_CREATION_CFORM, edition_descriptor=custom_forms.INVOICE_EDITION_CFORM, field_names=[ 'user', 'name', 'number', 'status', 'issuing_date', 'expiration_date', 'discount', 'currency', 'comment', 'additional_info', 'payment_terms', 'payment_type', 'buyers_order_number', ], ) build_custom_form_items( model=Quote, creation_descriptor=custom_forms.QUOTE_CREATION_CFORM, edition_descriptor=custom_forms.QUOTE_EDITION_CFORM, field_names=[ 'user', 'name', 'number', 'status', 'issuing_date', 'expiration_date', 'discount', 'currency', 'comment', 'additional_info', 'payment_terms', 'acceptation_date', ], ) build_custom_form_items( model=SalesOrder, creation_descriptor=custom_forms.ORDER_CREATION_CFORM, edition_descriptor=custom_forms.ORDER_EDITION_CFORM, field_names=[ 'user', 'name', 'number', 'status', 'issuing_date', 'expiration_date', 'discount', 'currency', 'comment', 'additional_info', 'payment_terms', ], ) build_custom_form_items( model=CreditNote, creation_descriptor=custom_forms.CNOTE_CREATION_CFORM, edition_descriptor=custom_forms.CNOTE_EDITION_CFORM, field_names=[ 'user', 'name', 'number', 'status', 'issuing_date', 'expiration_date', 'discount', 'currency', 'comment', 'additional_info', 'payment_terms', ], ) common_template_groups_desc = [ { 'name': _('General information'), 'cells': [ (EntityCellRegularField, { 'name': 'user' }), (EntityCellRegularField, { 'name': 'name' }), (EntityCellRegularField, { 'name': 'number' }), BillingTemplateStatusSubCell( model=TemplateBase).into_cell(), (EntityCellRegularField, { 'name': 'issuing_date' }), (EntityCellRegularField, { 'name': 'expiration_date' }), (EntityCellRegularField, { 'name': 'discount' }), (EntityCellRegularField, { 'name': 'currency' }), (EntityCellRegularField, { 'name': 'comment' }), (EntityCellRegularField, { 'name': 'additional_info' }), (EntityCellRegularField, { 'name': 'payment_terms' }), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.REMAINING_REGULARFIELDS }, ), ], }, { 'name': _('Organisations'), 'cells': [ BillingSourceSubCell(model=TemplateBase).into_cell(), BillingTargetSubCell(model=TemplateBase).into_cell(), ], }, { 'name': _('Description'), 'cells': [ (EntityCellRegularField, { 'name': 'description' }), ], }, { 'name': _('Custom fields'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.REMAINING_CUSTOMFIELDS }, ), ], }, ] CustomFormConfigItem.objects.create_if_needed( descriptor=custom_forms.BTEMPLATE_CREATION_CFORM, groups_desc=[ *common_template_groups_desc, *creation_only_groups_desc, ], ) CustomFormConfigItem.objects.create_if_needed( descriptor=custom_forms.BTEMPLATE_EDITION_CFORM, groups_desc=common_template_groups_desc, ) # --------------------------- get_ct = ContentType.objects.get_for_model engine_id = '' flavour_id = '' if 'creme.billing.exporters.xhtml2pdf.Xhtml2pdfExportEngine' in settings.BILLING_EXPORTERS: from creme.billing.exporters.xhtml2pdf import Xhtml2pdfExportEngine from creme.creme_core.utils import l10n # TODO: add the country in settings & use it... country = l10n.FR language = 'fr_FR' theme = 'cappuccino' try: Xhtml2pdfExportEngine.FLAVOURS_INFO[country][language][theme] except KeyError: pass else: engine_id = Xhtml2pdfExportEngine.id flavour_id = f'{country}/{language}/{theme}' for model in (CreditNote, Invoice, Quote, SalesOrder, TemplateBase): ExporterConfigItem.objects.get_or_create( content_type=get_ct(model), defaults={ 'engine_id': engine_id, 'flavour_id': flavour_id, }, ) # --------------------------- for model in (Invoice, CreditNote, Quote, SalesOrder): SearchConfigItem.objects.create_if_needed( model, ['name', 'number', 'status__name']) for model in (ProductLine, ServiceLine): SearchConfigItem.objects.create_if_needed(model, [], disabled=True) # --------------------------- create_svalue = SettingValue.objects.get_or_create create_svalue(key_id=setting_keys.payment_info_key.id, defaults={'value': True}) create_svalue(key_id=setting_keys.button_redirection_key.id, defaults={'value': True}) # --------------------------- # TODO: move to "not already_populated" section in creme2.4 if not MenuConfigItem.objects.filter( entry_id__startswith='billing-').exists(): container = MenuConfigItem.objects.get_or_create( entry_id=ContainerEntry.id, entry_data={'label': _('Management')}, defaults={'order': 50}, )[0] create_mitem = partial(MenuConfigItem.objects.create, parent=container) create_mitem(entry_id=menu.QuotesEntry.id, order=10) create_mitem(entry_id=menu.InvoicesEntry.id, order=15) create_mitem(entry_id=menu.CreditNotesEntry.id, order=50) create_mitem(entry_id=menu.SalesOrdersEntry.id, order=55) create_mitem(entry_id=menu.ProductLinesEntry.id, order=200) create_mitem(entry_id=menu.ServiceLinesEntry.id, order=210) # --------------------------- if not already_populated: def create_quote_status(pk, name, **kwargs): create_if_needed(QuoteStatus, {'pk': pk}, name=name, **kwargs) # Default status create_quote_status(1, pgettext('billing-quote', 'Pending'), order=2) create_quote_status(2, pgettext('billing-quote', 'Accepted'), order=3, won=True) create_quote_status(3, pgettext('billing-quote', 'Rejected'), order=4) create_quote_status(4, pgettext('billing-quote', 'Created'), order=1) # --------------------------- create_if_needed(SettlementTerms, {'pk': 1}, name=_('30 days')) create_if_needed(SettlementTerms, {'pk': 2}, name=_('Cash')) create_if_needed(SettlementTerms, {'pk': 3}, name=_('45 days')) create_if_needed(SettlementTerms, {'pk': 4}, name=_('60 days')) create_if_needed(SettlementTerms, {'pk': 5}, name=_('30 days, end month the 10')) # --------------------------- create_if_needed( AdditionalInformation, {'pk': 1}, name=_('Trainer accreditation'), description= _('being certified trainer courses could be supported by your OPCA' )) # --------------------------- create_bmi = ButtonMenuItem.objects.create_if_needed create_bmi(model=Invoice, button=buttons.GenerateInvoiceNumberButton, order=0) create_bmi(model=Quote, button=buttons.ConvertToInvoiceButton, order=0) create_bmi(model=Quote, button=buttons.ConvertToSalesOrderButton, order=1) create_bmi(model=SalesOrder, button=buttons.ConvertToInvoiceButton, order=0) create_bmi(model=Organisation, button=buttons.AddQuoteButton, order=100) create_bmi(model=Organisation, button=buttons.AddSalesOrderButton, order=101) create_bmi(model=Organisation, button=buttons.AddInvoiceButton, order=102) create_bmi(model=Contact, button=buttons.AddQuoteButton, order=100) create_bmi(model=Contact, button=buttons.AddSalesOrderButton, order=101) create_bmi(model=Contact, button=buttons.AddInvoiceButton, order=102) # --------------------------- create_cbci = CustomBrickConfigItem.objects.create build_cell = EntityCellRegularField.build def build_cells(model, *extra_cells): return [ build_cell(model, 'name'), build_cell(model, 'number'), build_cell(model, 'issuing_date'), build_cell(model, 'expiration_date'), build_cell(model, 'discount'), build_cell(model, 'additional_info'), build_cell(model, 'payment_terms'), build_cell(model, 'currency'), *extra_cells, build_cell(model, 'comment'), build_cell(model, 'description'), # -- build_cell(model, 'created'), build_cell(model, 'modified'), build_cell(model, 'user'), ] cbci_invoice = create_cbci( id='billing-invoice_info', name=_('Invoice information'), content_type=Invoice, cells=build_cells( Invoice, build_cell(Invoice, 'status'), build_cell(Invoice, 'payment_type'), build_cell(Invoice, 'buyers_order_number'), ), ) cbci_c_note = create_cbci( id='billing-creditnote_info', name=_('Credit note information'), content_type=CreditNote, cells=build_cells( CreditNote, build_cell(CreditNote, 'status'), ), ) cbci_quote = create_cbci( id='billing-quote_info', name=_('Quote information'), content_type=Quote, cells=build_cells( Quote, build_cell(Quote, 'status'), build_cell(Quote, 'acceptation_date'), ), ) cbci_s_order = create_cbci( id='billing-salesorder_info', name=_('Salesorder information'), content_type=SalesOrder, cells=build_cells( SalesOrder, build_cell(SalesOrder, 'status'), ), ) cbci_tbase = create_cbci( id='billing-templatebase_info', name=pgettext('billing', 'Template information'), content_type=TemplateBase, cells=build_cells( TemplateBase, EntityCellFunctionField.build(TemplateBase, 'get_verbose_status'), ), ) models_4_blocks = [ (Invoice, cbci_invoice, True), # Boolean -> insert CreditNote block (CreditNote, cbci_c_note, False), (Quote, cbci_quote, True), (SalesOrder, cbci_s_order, True), (TemplateBase, cbci_tbase, False), ] TOP = BrickDetailviewLocation.TOP LEFT = BrickDetailviewLocation.LEFT RIGHT = BrickDetailviewLocation.RIGHT for model, cbci, has_credit_notes in models_4_blocks: data = [ # LEFT { 'brick': cbci.brick_id, 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': bricks.BillingPaymentInformationBrick, 'order': 60 }, { 'brick': bricks.BillingPrettyAddressBrick, 'order': 70 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': bricks.TargetBrick, 'order': 2, 'zone': RIGHT }, { 'brick': bricks.TotalBrick, 'order': 3, 'zone': RIGHT }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, { 'brick': bricks.ProductLinesBrick, 'order': 10, 'zone': TOP }, { 'brick': bricks.ServiceLinesBrick, 'order': 20, 'zone': TOP }, ] if has_credit_notes: data.append({ 'brick': bricks.CreditNotesBrick, 'order': 30, 'zone': TOP }) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': model, 'zone': LEFT }, data=data, ) if apps.is_installed('creme.assistants'): logger.info( 'Assistants app is installed => we use the assistants blocks on detail views' ) from creme.assistants import bricks as a_bricks for t in models_4_blocks: BrickDetailviewLocation.objects.multi_create( defaults={ 'model': t[0], 'zone': RIGHT }, data=[ { 'brick': a_bricks.TodosBrick, 'order': 100 }, { 'brick': a_bricks.MemosBrick, 'order': 200 }, { 'brick': a_bricks.AlertsBrick, 'order': 300 }, { 'brick': a_bricks.UserMessagesBrick, 'order': 400 }, ], ) if apps.is_installed('creme.documents'): # logger.info( # 'Documents app is installed # => we use the documents block on detail views' # ) from creme.documents.bricks import LinkedDocsBrick for t in models_4_blocks: BrickDetailviewLocation.objects.create_if_needed( brick=LinkedDocsBrick, order=600, zone=RIGHT, model=t[0], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Organisation, 'zone': RIGHT }, data=[ { 'brick': bricks.PaymentInformationBrick, 'order': 300, 'zone': LEFT }, { 'brick': bricks.ReceivedInvoicesBrick, 'order': 14 }, { 'brick': bricks.ReceivedQuotesBrick, 'order': 18 }, ], ) # --------------------------- if apps.is_installed('creme.reports'): logger.info( 'Reports app is installed ' '=> we create 2 billing reports, with 3 graphs, and related blocks in home' ) self.create_reports( rt_sub_bill_received, current_year_invoice_filter, current_year_unpaid_invoice_filter, )
def populate(self): already_populated = RelationType.objects.filter(pk=constants.REL_SUB_BILL_ISSUED).exists() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() # --------------------------- # line_entities = [ProductLine, ServiceLine] line_entities = list(lines_registry) RelationType.create((constants.REL_SUB_BILL_ISSUED, _('issued by'), BILLING_MODELS), (constants.REL_OBJ_BILL_ISSUED, _('has issued'), [Organisation]), is_internal=True, minimal_display=(False, True), ) rt_sub_bill_received = \ RelationType.create((constants.REL_SUB_BILL_RECEIVED, _('received by'), BILLING_MODELS), (constants.REL_OBJ_BILL_RECEIVED, _('has received'), [Organisation, Contact]), is_internal=True, minimal_display=(False, True), )[0] RelationType.create((constants.REL_SUB_HAS_LINE, _('had the line'), BILLING_MODELS), (constants.REL_OBJ_HAS_LINE, _('is the line of'), line_entities), is_internal=True, minimal_display=(True, True), ) RelationType.create((constants.REL_SUB_LINE_RELATED_ITEM, _('has the related item'), line_entities), (constants.REL_OBJ_LINE_RELATED_ITEM, _('is the related item of'), [Product, Service]), is_internal=True, ) RelationType.create((constants.REL_SUB_CREDIT_NOTE_APPLIED, _('is used in the billing document'), [CreditNote]), (constants.REL_OBJ_CREDIT_NOTE_APPLIED, _('used the credit note'), [Quote, SalesOrder, Invoice]), is_internal=True, minimal_display=(True, True), ) if apps.is_installed('creme.activities'): logger.info('Activities app is installed => an Invoice/Quote/SalesOrder can be the subject of an Activity') from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT RelationType.objects.get(pk=REL_SUB_ACTIVITY_SUBJECT) \ .add_subject_ctypes(Invoice, Quote, SalesOrder) # --------------------------- create_if_needed(PaymentTerms, {'pk': 1}, name=_('Deposit'), description=_(r'20% deposit will be required'), is_custom=False, ) # --------------------------- # NB: pk=1 + is_custom=False --> default status (used when a quote is converted in invoice for example) create_if_needed(SalesOrderStatus, {'pk': 1}, name=pgettext('billing-salesorder', 'Issued'), order=1, is_custom=False) # Default status if not already_populated: create_if_needed(SalesOrderStatus, {'pk': 2}, name=pgettext('billing-salesorder', 'Accepted'), order=3) create_if_needed(SalesOrderStatus, {'pk': 3}, name=pgettext('billing-salesorder', 'Rejected'), order=4) create_if_needed(SalesOrderStatus, {'pk': 4}, name=pgettext('billing-salesorder', 'Created'), order=2) # --------------------------- def create_invoice_status(pk, name, **kwargs): create_if_needed(InvoiceStatus, {'pk': pk}, name=name, **kwargs) create_invoice_status(1, pgettext('billing-invoice', 'Draft'), order=1, is_custom=False) # Default status create_invoice_status(2, pgettext('billing-invoice', 'To be sent'), order=2, is_custom=False) if not already_populated: create_invoice_status(3, pgettext('billing-invoice', 'Sent'), order=3, pending_payment=True) create_invoice_status(4, pgettext('billing-invoice', 'Resulted'), order=5) create_invoice_status(5, pgettext('billing-invoice', 'Partly resulted'), order=4, pending_payment=True) create_invoice_status(6, _('Collection'), order=7) create_invoice_status(7, _('Resulted collection'), order=6) create_invoice_status(8, pgettext('billing-invoice', 'Canceled'), order=8) # --------------------------- create_if_needed(CreditNoteStatus, {'pk': 1}, name=pgettext('billing-creditnote', 'Draft'), order=1, is_custom=False) if not already_populated: create_if_needed(CreditNoteStatus, {'pk': 2}, name=pgettext('billing-creditnote', 'Issued'), order=2) create_if_needed(CreditNoteStatus, {'pk': 3}, name=pgettext('billing-creditnote', 'Consumed'), order=3) create_if_needed(CreditNoteStatus, {'pk': 4}, name=pgettext('billing-creditnote', 'Out of date'), order=4) # --------------------------- EntityFilter.create( 'billing-invoices_unpaid', name=_('Invoices unpaid'), model=Invoice, user='******', conditions=[EntityFilterCondition.build_4_field( model=Invoice, operator=EntityFilterCondition.EQUALS, name='status__pending_payment', values=[True], ), ], ) EntityFilter.create( 'billing-invoices_unpaid_late', name=_('Invoices unpaid and late'), model=Invoice, user='******', conditions=[EntityFilterCondition.build_4_field( model=Invoice, operator=EntityFilterCondition.EQUALS, name='status__pending_payment', values=[True], ), EntityFilterCondition.build_4_date( model=Invoice, name='expiration_date', date_range='in_past', ), ], ) current_year_invoice_filter = EntityFilter.create( 'billing-current_year_invoices', name=_('Current year invoices'), model=Invoice, user='******', conditions=[EntityFilterCondition.build_4_date( model=Invoice, name='issuing_date', date_range='current_year', ), ], ) current_year_unpaid_invoice_filter = EntityFilter.create( 'billing-current_year_unpaid_invoices', name=_('Current year and unpaid invoices'), model=Invoice, user='******', conditions=[EntityFilterCondition.build_4_date( model=Invoice, name='issuing_date', date_range='current_year', ), EntityFilterCondition.build_4_field( model=Invoice, operator=EntityFilterCondition.EQUALS, name='status__pending_payment', values=[True], ), ], ) # --------------------------- def create_hf(hf_pk, name, model, status=True): HeaderFilter.create(pk=hf_pk, name=name, model=model, cells_desc=[(EntityCellRegularField, {'name': 'name'}), EntityCellRelation(model=model, rtype=rt_sub_bill_received), (EntityCellRegularField, {'name': 'number'}), (EntityCellRegularField, {'name': 'status'}) if status else None, (EntityCellRegularField, {'name': 'total_no_vat'}), (EntityCellRegularField, {'name': 'issuing_date'}), (EntityCellRegularField, {'name': 'expiration_date'}), ], ) create_hf(constants.DEFAULT_HFILTER_INVOICE, _('Invoice view'), Invoice) create_hf(constants.DEFAULT_HFILTER_QUOTE, _('Quote view'), Quote) create_hf(constants.DEFAULT_HFILTER_ORDER, _('Sales order view'), SalesOrder) create_hf(constants.DEFAULT_HFILTER_CNOTE, _('Credit note view'), CreditNote) create_hf(constants.DEFAULT_HFILTER_TEMPLATE, _('Template view'), TemplateBase, status=False) def create_hf_lines(hf_pk, name, model): build_cell = EntityCellRegularField.build HeaderFilter.create(pk=hf_pk, name=name, model=model, cells_desc=[build_cell(model=model, name='on_the_fly_item'), build_cell(model=model, name='quantity'), build_cell(model=model, name='unit_price'), ] ) create_hf_lines('billing-hg_product_lines', _('Product lines view'), ProductLine) create_hf_lines('billing-hg_service_lines', _('Service lines view'), ServiceLine) # --------------------------- for model in (Invoice, CreditNote, Quote, SalesOrder): SearchConfigItem.create_if_needed(model, ['name', 'number', 'status__name']) for model in (ProductLine, ServiceLine): SearchConfigItem.create_if_needed(model, [], disabled=True) # --------------------------- SettingValue.objects.get_or_create(key_id=setting_keys.payment_info_key.id, defaults={'value': True}) # --------------------------- if not already_populated: create_if_needed(QuoteStatus, {'pk': 1}, name=pgettext('billing-quote', 'Pending'), order=2) # Default status create_if_needed(QuoteStatus, {'pk': 2}, name=pgettext('billing-quote', 'Accepted'), order=3, won=True) create_if_needed(QuoteStatus, {'pk': 3}, name=pgettext('billing-quote', 'Rejected'), order=4) create_if_needed(QuoteStatus, {'pk': 4}, name=pgettext('billing-quote', 'Created'), order=1) # --------------------------- create_if_needed(SettlementTerms, {'pk': 1}, name=_('30 days')) create_if_needed(SettlementTerms, {'pk': 2}, name=_('Cash')) create_if_needed(SettlementTerms, {'pk': 3}, name=_('45 days')) create_if_needed(SettlementTerms, {'pk': 4}, name=_('60 days')) create_if_needed(SettlementTerms, {'pk': 5}, name=_('30 days, end month the 10')) # --------------------------- create_if_needed(AdditionalInformation, {'pk': 1}, name=_('Trainer accreditation'), description=_('being certified trainer courses could be supported by your OPCA') ) # --------------------------- create_bmi = ButtonMenuItem.create_if_needed create_bmi(pk='billing-generate_invoice_number', model=Invoice, button=buttons.GenerateInvoiceNumberButton, order=0) create_bmi(pk='billing-quote_orga_button', model=Organisation, button=buttons.AddQuoteButton, order=100) create_bmi(pk='billing-salesorder_orga_button', model=Organisation, button=buttons.AddSalesOrderButton, order=101) create_bmi(pk='billing-invoice_orga_button', model=Organisation, button=buttons.AddInvoiceButton, order=102) create_bmi(pk='billing-quote_contact_button', model=Contact, button=buttons.AddQuoteButton, order=100) create_bmi(pk='billing-salesorder_contact_button', model=Contact, button=buttons.AddSalesOrderButton, order=101) create_bmi(pk='billing-invoice_contact_button', model=Contact, button=buttons.AddInvoiceButton, order=102) # --------------------------- get_ct = ContentType.objects.get_for_model create_cbci = CustomBrickConfigItem.objects.create build_cell = EntityCellRegularField.build def build_cells(model, *extra_cells): return [ build_cell(model, 'name'), build_cell(model, 'number'), build_cell(model, 'issuing_date'), build_cell(model, 'expiration_date'), build_cell(model, 'discount'), build_cell(model, 'additional_info'), build_cell(model, 'payment_terms'), build_cell(model, 'currency'), ] + list(extra_cells) + [ build_cell(model, 'comment'), # -- build_cell(model, 'created'), build_cell(model, 'modified'), build_cell(model, 'user'), ] cbci_invoice = create_cbci(id='billing-invoice_info', name=_('Invoice information'), content_type=get_ct(Invoice), cells=build_cells(Invoice, build_cell(Invoice, 'status'), build_cell(Invoice, 'payment_type'), ), ) cbci_c_note = create_cbci(id='billing-creditnote_info', name=_('Credit note information'), content_type=get_ct(CreditNote), cells=build_cells(CreditNote, build_cell(CreditNote, 'status')), ) cbci_quote = create_cbci(id='billing-quote_info', name=_('Quote information'), content_type=get_ct(Quote), cells=build_cells(Quote, build_cell(Quote, 'status'), build_cell(Quote, 'acceptation_date'), ), ) cbci_s_order = create_cbci(id='billing-salesorder_info', name=_('Salesorder information'), content_type=get_ct(SalesOrder), cells=build_cells(SalesOrder, build_cell(SalesOrder, 'status')), ) cbci_tbase = create_cbci(id='billing-templatebase_info', name=pgettext('billing', 'Template information'), content_type=get_ct(TemplateBase), cells=build_cells(TemplateBase, EntityCellFunctionField.build(TemplateBase, 'get_verbose_status'), ), ) models_4_blocks = [(Invoice, cbci_invoice, True), # Boolean -> insert CreditNote block (CreditNote, cbci_c_note, False), (Quote, cbci_quote, True), (SalesOrder, cbci_s_order, True), (TemplateBase, cbci_tbase, False), ] create_bdl = BrickDetailviewLocation.create_if_needed TOP = BrickDetailviewLocation.TOP LEFT = BrickDetailviewLocation.LEFT RIGHT = BrickDetailviewLocation.RIGHT for model, cbci, has_credit_notes in models_4_blocks: create_bdl(brick_id=bricks.ProductLinesBrick.id_, order=10, zone=TOP, model=model) create_bdl(brick_id=bricks.ServiceLinesBrick.id_, order=20, zone=TOP, model=model) if has_credit_notes: create_bdl(brick_id=bricks.CreditNotesBrick.id_, order=30, zone=TOP, model=model) create_bdl(brick_id=cbci.generate_id(), order=5, zone=LEFT, model=model) create_bdl(brick_id=core_bricks.CustomFieldsBrick.id_, order=40, zone=LEFT, model=model) create_bdl(brick_id=bricks.BillingPaymentInformationBrick.id_, order=60, zone=LEFT, model=model) create_bdl(brick_id=bricks.BillingPrettyAddressBrick.id_, order=70, zone=LEFT, model=model) create_bdl(brick_id=core_bricks.PropertiesBrick.id_, order=450, zone=LEFT, model=model) create_bdl(brick_id=core_bricks.RelationsBrick.id_, order=500, zone=LEFT, model=model) create_bdl(brick_id=bricks.TargetBrick.id_, order=2, zone=RIGHT, model=model) create_bdl(brick_id=bricks.TotalBrick.id_, order=3, zone=RIGHT, model=model) create_bdl(brick_id=core_bricks.HistoryBrick.id_, order=20, zone=RIGHT, model=model) if apps.is_installed('creme.assistants'): logger.info('Assistants app is installed => we use the assistants blocks on detail views') from creme.assistants.bricks import AlertsBrick, MemosBrick, TodosBrick, UserMessagesBrick for t in models_4_blocks: model = t[0] create_bdl(brick_id=TodosBrick.id_, order=100, zone=RIGHT, model=model) create_bdl(brick_id=MemosBrick.id_, order=200, zone=RIGHT, model=model) create_bdl(brick_id=AlertsBrick.id_, order=300, zone=RIGHT, model=model) create_bdl(brick_id=UserMessagesBrick.id_, order=400, zone=RIGHT, model=model) if apps.is_installed('creme.documents'): # logger.info('Documents app is installed => we use the documents block on detail views') from creme.documents.bricks import LinkedDocsBrick for t in models_4_blocks: create_bdl(brick_id=LinkedDocsBrick.id_, order=600, zone=RIGHT, model=t[0]) create_bdl(brick_id=bricks.PaymentInformationBrick.id_, order=300, zone=LEFT, model=Organisation) create_bdl(brick_id=bricks.ReceivedInvoicesBrick.id_, order=14, zone=RIGHT, model=Organisation) create_bdl(brick_id=bricks.ReceivedQuotesBrick.id_, order=18, zone=RIGHT, model=Organisation) # --------------------------- if apps.is_installed('creme.reports'): logger.info('Reports app is installed => we create 2 billing reports, with 3 graphs, and related blocks in home') self.create_reports(rt_sub_bill_received, current_year_invoice_filter, current_year_unpaid_invoice_filter, )