def test_populate(self): # test get_compatible_ones() too get_ct = ContentType.objects.get_for_model ct = get_ct(Opportunity) relation_types = RelationType.objects.compatible(ct).in_bulk() Product = products.get_product_model() Service = products.get_service_model() self.assertNotIn(constants.REL_SUB_TARGETS, relation_types) self.get_relationtype_or_fail(constants.REL_SUB_TARGETS, [Opportunity], [Contact, Organisation]) self.assertNotIn(constants.REL_SUB_EMIT_ORGA, relation_types) self.get_relationtype_or_fail(constants.REL_OBJ_EMIT_ORGA, [Opportunity], [Organisation]) self.assertIn(constants.REL_OBJ_LINKED_PRODUCT, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_PRODUCT, relation_types) self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_PRODUCT, [Opportunity], [Product]) self.assertIn(constants.REL_OBJ_LINKED_SERVICE, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_SERVICE, relation_types) self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_SERVICE, [Opportunity], [Service]) self.assertIn(constants.REL_OBJ_LINKED_CONTACT, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_CONTACT, relation_types) self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_CONTACT, [Opportunity], [Contact]) self.assertIn(constants.REL_OBJ_RESPONSIBLE, relation_types) self.assertNotIn(constants.REL_SUB_RESPONSIBLE, relation_types) self.get_relationtype_or_fail(constants.REL_OBJ_RESPONSIBLE, [Opportunity], [Contact]) self.assertTrue(SalesPhase.objects.exists()) self.assertTrue(Origin.objects.exists()) # self.assertEqual(1, SettingValue.objects.filter(key_id=constants.SETTING_USE_CURRENT_QUOTE).count()) def assertSVEqual(key, value): with self.assertNoException(): sv = SettingValue.objects.get_4_key(key) self.assertIs(sv.value, value) assertSVEqual(setting_keys.quote_key, False) assertSVEqual(setting_keys.target_constraint_key, True) assertSVEqual(setting_keys.emitter_constraint_key, True) # Contribution to activities rtype = self.get_object_or_fail(RelationType, pk=REL_SUB_ACTIVITY_SUBJECT) self.assertTrue(rtype.subject_ctypes.filter(id=ct.id).exists()) self.assertTrue( rtype.subject_ctypes.filter(id=get_ct(Contact).id).exists()) self.assertTrue( rtype.symmetric_type.object_ctypes.filter(id=ct.id).exists())
def get_products(self): warnings.warn('AbstractOpportunity.get_products() is deprecated.', DeprecationWarning) from creme.products import get_product_model return get_product_model()\ .objects\ .filter(is_deleted=False, relations__object_entity=self.id, relations__type=constants.REL_SUB_LINKED_PRODUCT, )
class ProductLineMultipleAddForm(_LineMultipleAddForm): items = MultiCreatorEntityField(label=_('Products'), model=products.get_product_model()) blocks = core_forms.FieldBlockManager( ('general', _('Products choice'), ['items']), ('additional', _('Optional global information applied to your selected products'), ['quantity', 'vat', 'discount_value'])) def _get_line_class(self): return ProductLine
def test_linked_products(self): brick_cls = bricks.LinkedProductsBrick brick_cls.page_size = max(5, settings.BLOCK_SIZE) user = self.login() opp01, target, emitter = self._create_opportunity_n_organisations( name='Opp#1') opp02 = Opportunity.objects.create( user=self.user, name='Opp#2', sales_phase=opp01.sales_phase, emitter=emitter, target=target, ) sub_cat = SubCategory.objects.all()[0] create_product = partial( products.get_product_model().objects.create, user=user, unit_price=Decimal('1.23'), category=sub_cat.category, sub_category=sub_cat, ) product01 = create_product(name='Eva00') product02 = create_product(name='Eva01') product03 = create_product(name='Eva02') product04 = create_product(name='Eva03') product05 = create_product(name='Eva04', is_deleted=True) create_rel = partial(Relation.objects.create, user=user, type_id=constants.REL_OBJ_LINKED_PRODUCT) create_rel(subject_entity=opp01, object_entity=product01) create_rel(subject_entity=opp01, object_entity=product02) create_rel(subject_entity=opp02, object_entity=product04) create_rel(subject_entity=opp01, object_entity=product05) response = self.assertGET200(opp01.get_absolute_url()) tree = self.get_html_tree(response.content) brick_node = self.get_brick_node(tree, brick_cls.id_) self.assertInstanceLink(brick_node, product01) self.assertInstanceLink(brick_node, product02) self.assertNoInstanceLink(brick_node, product03) self.assertNoInstanceLink(brick_node, product04) self.assertNoInstanceLink(brick_node, product05)
def post(self, request, *args, **kwargs): klass = self.get_ctype().model_class() try: rtype_id, set_as_current, workflow_action = self.behaviours[klass] except KeyError as e: raise Http404('Bad billing document type') from e user = request.user user.has_perm_to_create_or_die(klass) # TODO: check in template too (must upgrade 'has_perm' to use owner!=None) user.has_perm_to_link_or_die(klass, owner=user) opp = self.get_related_entity() b_document = klass.objects.create( user=user, issuing_date=now(), status_id=1, currency=opp.currency, source=opp.emitter, target=opp.target, ) create_relation = partial( Relation.objects.create, subject_entity=b_document, user=user, ) create_relation(type_id=rtype_id, object_entity=opp) b_document.generate_number( ) # Need the relationship with emitter organisation b_document.name = self.generated_name.format(document=b_document, opportunity=opp) b_document.save() relations = Relation.objects.filter( subject_entity=opp.id, type__in=[ constants.REL_OBJ_LINKED_PRODUCT, constants.REL_OBJ_LINKED_SERVICE, ], ).select_related('object_entity') # TODO: Missing test case if relations: Relation.populate_real_object_entities(relations) vat_value = Vat.get_default_vat() Product = get_product_model() for relation in relations: item = relation.object_entity.get_real_entity() line_klass = ProductLine if isinstance( item, Product) else ServiceLine line_klass.objects.create( related_item=item, related_document=b_document, unit_price=item.unit_price, unit=item.unit, vat_value=vat_value, ) if set_as_current: create_relation(type_id=constants.REL_SUB_CURRENT_DOC, object_entity=opp) if workflow_action: workflow_action(opp.emitter, opp.target, user) # if request.is_ajax(): if is_ajax(request): return HttpResponse() return redirect(opp)
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, )
# -*- coding: utf-8 -*- from django.utils.translation import gettext_lazy as _ from creme import products from creme.creme_core.gui.custom_form import CustomFormDescriptor from creme.products.forms.base import SubCategorySubCell Product = products.get_product_model() Service = products.get_service_model() PRODUCT_CREATION_CFORM = CustomFormDescriptor( id='products-product_creation', model=Product, verbose_name=_('Creation form for product'), excluded_fields=('category', 'sub_category'), extra_sub_cells=[SubCategorySubCell(model=Product)], ) PRODUCT_EDITION_CFORM = CustomFormDescriptor( id='products-product_edition', model=Product, form_type=CustomFormDescriptor.EDITION_FORM, verbose_name=_('Edition form for product'), excluded_fields=('category', 'sub_category', 'images'), extra_sub_cells=[SubCategorySubCell(model=Product)], ) SERVICE_CREATION_CFORM = CustomFormDescriptor( id='products-service_creation', model=Service, verbose_name=_('Creation form for service'), excluded_fields=('category', 'sub_category'),
CreditNote = billing.get_credit_note_model() Invoice = billing.get_invoice_model() Quote = billing.get_quote_model() SalesOrder = billing.get_sales_order_model() TemplateBase = billing.get_template_base_model() ProductLine = billing.get_product_line_model() ServiceLine = billing.get_service_line_model() except Exception as e: print('Error in <{}>: {}'.format(__name__, e)) Address = get_address_model() Contact = get_contact_model() Organisation = get_organisation_model() Product = get_product_model() Service = get_service_model() def skipIfCustomCreditNote(test_func): return skipIf(skip_cnote_tests, 'Custom CreditNote model in use')(test_func) def skipIfCustomInvoice(test_func): return skipIf(skip_invoice_tests, 'Custom Invoice model in use')(test_func) def skipIfCustomQuote(test_func): return skipIf(skip_quote_tests, 'Custom Quote model in use')(test_func)
def test_populate(self): # test get_compatible_ones() too ct = ContentType.objects.get_for_model(Opportunity) relation_types = RelationType.objects.compatible(ct).in_bulk() Product = products.get_product_model() Service = products.get_service_model() self.assertNotIn(constants.REL_SUB_TARGETS, relation_types) self.get_relationtype_or_fail( constants.REL_SUB_TARGETS, [Opportunity], [Contact, Organisation], ) self.assertNotIn(constants.REL_SUB_EMIT_ORGA, relation_types) self.get_relationtype_or_fail( constants.REL_OBJ_EMIT_ORGA, [Opportunity], [Organisation], ) self.assertIn(constants.REL_OBJ_LINKED_PRODUCT, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_PRODUCT, relation_types) self.get_relationtype_or_fail( constants.REL_OBJ_LINKED_PRODUCT, [Opportunity], [Product], ) self.assertIn(constants.REL_OBJ_LINKED_SERVICE, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_SERVICE, relation_types) self.get_relationtype_or_fail( constants.REL_OBJ_LINKED_SERVICE, [Opportunity], [Service], ) self.assertIn(constants.REL_OBJ_LINKED_CONTACT, relation_types) self.assertNotIn(constants.REL_SUB_LINKED_CONTACT, relation_types) self.get_relationtype_or_fail( constants.REL_OBJ_LINKED_CONTACT, [Opportunity], [Contact], ) self.assertIn(constants.REL_OBJ_RESPONSIBLE, relation_types) self.assertNotIn(constants.REL_SUB_RESPONSIBLE, relation_types) self.get_relationtype_or_fail( constants.REL_OBJ_RESPONSIBLE, [Opportunity], [Contact], ) self.assertTrue(SalesPhase.objects.exists()) self.assertTrue(Origin.objects.exists()) def assertSVEqual(key, value): with self.assertNoException(): sv = SettingValue.objects.get_4_key(key) self.assertIs(sv.value, value) assertSVEqual(setting_keys.quote_key, False) assertSVEqual(setting_keys.target_constraint_key, True) assertSVEqual(setting_keys.emitter_constraint_key, True)
def related_item_class(): return get_product_model()
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, )
def populate(self): already_populated = RelationType.objects.filter( pk=constants.REL_SUB_SOLD).exists() Act = commercial.get_act_model() ActObjectivePattern = commercial.get_pattern_model() Strategy = commercial.get_strategy_model() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() RelationType.create( (constants.REL_SUB_SOLD, _('has sold'), [Contact, Organisation]), (constants.REL_OBJ_SOLD, _('has been sold by'), [Product, Service ]), ) complete_goal_models = {*creme_registry.iter_entity_models()} complete_goal_models.discard(Strategy) if apps.is_installed('creme.billing'): from creme import billing from creme.billing.registry import lines_registry complete_goal_models.discard(billing.get_credit_note_model()) complete_goal_models.discard(billing.get_template_base_model()) complete_goal_models.difference_update(lines_registry) RelationType.create( ( constants.REL_SUB_COMPLETE_GOAL, _('completes a goal of the commercial action'), complete_goal_models, ), ( constants.REL_OBJ_COMPLETE_GOAL, _('is completed thanks to'), [Act], ), ) # --------------------------- CremePropertyType.create(constants.PROP_IS_A_SALESMAN, _('is a salesman'), [Contact]) # --------------------------- MarketSegment.objects.get_or_create( property_type=None, defaults={'name': _('All the organisations')}, ) # --------------------------- for i, title in enumerate( [_('Phone calls'), _('Show'), _('Demo')], start=1): create_if_needed(ActType, {'pk': i}, title=title, is_custom=False) # --------------------------- create_hf = HeaderFilter.objects.create_if_needed create_hf( pk=constants.DEFAULT_HFILTER_ACT, model=Act, name=_('Com Action view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), (EntityCellRegularField, { 'name': 'expected_sales' }), (EntityCellRegularField, { 'name': 'due_date' }), ], ) create_hf( pk=constants.DEFAULT_HFILTER_STRATEGY, model=Strategy, name=_('Strategy view'), cells_desc=[(EntityCellRegularField, { 'name': 'name' })], ) create_hf( pk=constants.DEFAULT_HFILTER_PATTERN, model=ActObjectivePattern, name=_('Objective pattern view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), (EntityCellRegularField, { 'name': 'segment' }), ], ) # --------------------------- def build_custom_form_items(creation_descriptor, edition_descriptor, field_names): base_groups_desc = [ { 'name': _('General information'), 'layout': LAYOUT_DUAL_FIRST, 'cells': [ *((EntityCellRegularField, { 'name': fname }) for fname in field_names), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_REGULARFIELDS }, ), ], }, { '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_desc, { 'name': _('Properties'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. CREME_PROPERTIES }, ), ], }, { 'name': _('Relationships'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.RELATIONS }, ), ], }, ], ) CustomFormConfigItem.objects.create_if_needed( descriptor=edition_descriptor, groups_desc=base_groups_desc, ) build_custom_form_items( creation_descriptor=custom_forms.ACT_CREATION_CFORM, edition_descriptor=custom_forms.ACT_EDITION_CFORM, field_names=[ 'user', 'name', 'expected_sales', 'cost', 'goal', 'start', 'due_date', 'act_type', 'segment', ], ) build_custom_form_items( creation_descriptor=custom_forms.PATTERN_CREATION_CFORM, edition_descriptor=custom_forms.PATTERN_EDITION_CFORM, field_names=[ 'user', 'name', 'average_sales', 'segment', ], ) build_custom_form_items( creation_descriptor=custom_forms.STRATEGY_CREATION_CFORM, edition_descriptor=custom_forms.STRATEGY_EDITION_CFORM, field_names=[ 'user', 'name', ], ) # --------------------------- create_searchconf = SearchConfigItem.objects.create_if_needed create_searchconf(Act, ['name', 'expected_sales', 'cost', 'goal']) create_searchconf(Strategy, ['name']) create_searchconf(ActObjectivePattern, [], disabled=True) # --------------------------- SettingValue.objects.get_or_create( key_id=setting_keys.orga_approaches_key.id, defaults={'value': True}, ) # --------------------------- Job.objects.get_or_create( type_id=creme_jobs.com_approaches_emails_send_type.id, defaults={ 'language': settings.LANGUAGE_CODE, 'periodicity': date_period_registry.get_period('days', 1), 'status': Job.STATUS_OK, # The CommercialApproach field for Activities' CustomForms is not # in the default configuration, so a enabled job would be annoying. 'enabled': False, }, ) # --------------------------- # TODO: move to "not already_populated" section in creme2.4 if not MenuConfigItem.objects.filter( entry_id__startswith='commercial-').exists(): container = MenuConfigItem.objects.get_or_create( entry_id=ContainerEntry.id, entry_data={'label': _('Commercial')}, defaults={'order': 30}, )[0] create_mitem = MenuConfigItem.objects.create create_mitem(entry_id=menu.ActsEntry.id, order=50, parent=container) create_mitem(entry_id=menu.StrategiesEntry.id, order=55, parent=container) create_mitem(entry_id=menu.SegmentsEntry.id, order=60, parent=container) create_mitem(entry_id=menu.PatternsEntry.id, order=70, parent=container) directory = MenuConfigItem.objects.filter( entry_id=ContainerEntry.id, entry_data={ 'label': _('Directory') }, ).first() if directory is not None: create_mitem(entry_id=menu.SalesmenEntry.id, order=100, parent=directory) creations = MenuConfigItem.objects.filter( entry_id=ContainerEntry.id, entry_data={ 'label': _('+ Creation') }, ).first() if creations is not None: create_mitem(entry_id=menu.ActCreationEntry.id, order=40, parent=creations) # --------------------------- if not already_populated: ButtonMenuItem.objects.create_if_needed( button=buttons.CompleteGoalButton, order=60, ) TOP = BrickDetailviewLocation.TOP RIGHT = BrickDetailviewLocation.RIGHT LEFT = BrickDetailviewLocation.LEFT # BrickDetailviewLocation.objects.multi_create( # defaults={'brick': bricks.ApproachesBrick, 'order': 10, 'zone': RIGHT}, # data=[ # {}, # default configuration # {'model': Contact}, # {'model': Organisation}, # ] # ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Act, 'zone': LEFT }, data=[ { 'order': 5 }, # generic information brick { 'brick': bricks.ActObjectivesBrick, 'order': 10 }, { 'brick': bricks.RelatedOpportunitiesBrick, 'order': 20 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': ActObjectivePattern, 'zone': LEFT }, data=[ { 'brick': bricks.PatternComponentsBrick, 'order': 10, 'zone': TOP }, { 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Strategy, 'zone': LEFT }, data=[ { 'brick': bricks.SegmentDescriptionsBrick, 'order': 10, 'zone': TOP }, { 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': bricks.EvaluatedOrgasBrick, 'order': 50 }, { 'brick': bricks.AssetsBrick, 'order': 60 }, { 'brick': bricks.CharmsBrick, 'order': 70 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) 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 model in (Act, ActObjectivePattern, Strategy): BrickDetailviewLocation.objects.multi_create( defaults={ 'model': model, '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 blocks on Strategy's detail views") from creme.documents.bricks import LinkedDocsBrick BrickDetailviewLocation.objects.multi_create( defaults={ 'brick': LinkedDocsBrick, 'order': 600, 'zone': RIGHT }, data=[ { 'model': Act }, { 'model': ActObjectivePattern }, { 'model': Strategy }, ], )
def populate(self): already_populated = RelationType.objects.filter( pk=constants.REL_SUB_TARGETS).exists() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() # --------------------------- create_rtype = RelationType.create rt_sub_targets = create_rtype( ( constants.REL_SUB_TARGETS, _('targets the organisation/contact'), [Opportunity], ), ( constants.REL_OBJ_TARGETS, _('targeted by the opportunity'), [Organisation, Contact], ), is_internal=True, minimal_display=(True, True), )[0] rt_obj_emit_orga = create_rtype( ( constants.REL_SUB_EMIT_ORGA, _('has generated the opportunity'), [Organisation], ), ( constants.REL_OBJ_EMIT_ORGA, _('has been generated by'), [Opportunity], ), is_internal=True, minimal_display=(True, True), )[1] create_rtype((constants.REL_SUB_LINKED_PRODUCT, _('is linked to the opportunity'), [Product]), (constants.REL_OBJ_LINKED_PRODUCT, _('concerns the product'), [Opportunity])) create_rtype( (constants.REL_SUB_LINKED_SERVICE, _('is linked to the opportunity'), [Service]), (constants.REL_OBJ_LINKED_SERVICE, _('concerns the service'), [Opportunity]), ) create_rtype( (constants.REL_SUB_LINKED_CONTACT, _('involves in the opportunity'), [Contact]), (constants.REL_OBJ_LINKED_CONTACT, _('stages'), [Opportunity])), create_rtype( (constants.REL_SUB_RESPONSIBLE, _('is responsible for'), [Contact ]), (constants.REL_OBJ_RESPONSIBLE, _('has as responsible contact'), [Opportunity]), ) if apps.is_installed('creme.activities'): logger.info('Activities app is installed' ' => an Opportunity 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(Opportunity) if apps.is_installed('creme.billing'): logger.info( 'Billing app is installed' ' => we create relationships between Opportunities & billing models' ) from creme import billing Invoice = billing.get_invoice_model() Quote = billing.get_quote_model() SalesOrder = billing.get_sales_order_model() create_rtype( ( constants.REL_SUB_LINKED_SALESORDER, _('is associate with the opportunity'), [SalesOrder], ), (constants.REL_OBJ_LINKED_SALESORDER, _('has generated the salesorder'), [Opportunity]), ) create_rtype( ( constants.REL_SUB_LINKED_INVOICE, pgettext('opportunities-invoice', 'generated for the opportunity'), [Invoice], ), ( constants.REL_OBJ_LINKED_INVOICE, _('has resulted in the invoice'), [Opportunity], ), ) create_rtype( ( constants.REL_SUB_LINKED_QUOTE, pgettext('opportunities-quote', 'generated for the opportunity'), [Quote], ), ( constants.REL_OBJ_LINKED_QUOTE, _('has resulted in the quote'), [Opportunity], ), ) create_rtype( ( constants.REL_SUB_CURRENT_DOC, _('is the current accounting document of'), [SalesOrder, Invoice, Quote], ), ( constants.REL_OBJ_CURRENT_DOC, _('has as current accounting document'), [Opportunity], ), ) # --------------------------- create_sv = SettingValue.objects.get_or_create create_sv(key_id=setting_keys.quote_key.id, defaults={'value': False}) create_sv(key_id=setting_keys.target_constraint_key.id, defaults={'value': True}) create_sv(key_id=setting_keys.emitter_constraint_key.id, defaults={'value': True}) # --------------------------- create_efilter = partial( EntityFilter.objects.smart_update_or_create, model=Opportunity, user='******', ) build_cond = partial( condition_handler.RegularFieldConditionHandler.build_condition, model=Opportunity, ) create_efilter( 'opportunities-opportunities_won', name=_('Opportunities won'), conditions=[ build_cond( operator=operators.EqualsOperator, field_name='sales_phase__won', values=[True], ), ], ) create_efilter( 'opportunities-opportunities_lost', name=_('Opportunities lost'), conditions=[ build_cond( operator=operators.EqualsOperator, field_name='sales_phase__lost', values=[True], ), ], ) create_efilter( 'opportunities-neither_won_nor_lost_opportunities', name=_('Neither won nor lost opportunities'), conditions=[ build_cond( operator=operators.EqualsNotOperator, field_name='sales_phase__won', values=[True], ), build_cond( operator=operators.EqualsNotOperator, field_name='sales_phase__lost', values=[True], ), ], ) # --------------------------- HeaderFilter.objects.create_if_needed( pk=constants.DEFAULT_HFILTER_OPPORTUNITY, model=Opportunity, name=_('Opportunity view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), EntityCellRelation(model=Opportunity, rtype=rt_sub_targets), (EntityCellRegularField, { 'name': 'sales_phase' }), (EntityCellRegularField, { 'name': 'estimated_sales' }), (EntityCellRegularField, { 'name': 'made_sales' }), (EntityCellRegularField, { 'name': 'closing_date' }), ], ) # --------------------------- common_groups_desc = [ { 'name': _('Description'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ (EntityCellRegularField, { 'name': 'description' }), ], }, { 'name': _('Custom fields'), 'layout': LAYOUT_DUAL_SECOND, 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.REMAINING_CUSTOMFIELDS }, ), ], }, ] common_field_names = [ 'reference', 'estimated_sales', 'made_sales', 'currency', 'sales_phase', 'chance_to_win', 'expected_closing_date', 'closing_date', 'origin', 'first_action_date', ] CustomFormConfigItem.objects.create_if_needed( descriptor=custom_forms.OPPORTUNITY_CREATION_CFORM, groups_desc=[ { 'name': _('General information'), 'layout': LAYOUT_DUAL_FIRST, 'cells': [ (EntityCellRegularField, { 'name': 'user' }), (EntityCellRegularField, { 'name': 'name' }), OppEmitterSubCell().into_cell(), OppTargetSubCell().into_cell(), *((EntityCellRegularField, { 'name': fname }) for fname in common_field_names), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_REGULARFIELDS }, ), ], }, *common_groups_desc, { 'name': _('Properties'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.CREME_PROPERTIES }, ), ], }, { 'name': _('Relationships'), 'cells': [ ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial.RELATIONS }, ), ], }, ], ) CustomFormConfigItem.objects.create_if_needed( descriptor=custom_forms.OPPORTUNITY_EDITION_CFORM, groups_desc=[ { 'name': _('General information'), 'cells': [ (EntityCellRegularField, { 'name': 'user' }), (EntityCellRegularField, { 'name': 'name' }), OppTargetSubCell().into_cell(), *((EntityCellRegularField, { 'name': fname }) for fname in common_field_names), ( EntityCellCustomFormSpecial, { 'name': EntityCellCustomFormSpecial. REMAINING_REGULARFIELDS }, ), ], }, *common_groups_desc, ], ) # --------------------------- SearchConfigItem.objects.create_if_needed( Opportunity, ['name', 'made_sales', 'sales_phase__name', 'origin__name'], ) # --------------------------- # TODO: move to "not already_populated" section in creme2.4 if not MenuConfigItem.objects.filter( entry_id__startswith='opportunities-').exists(): container = MenuConfigItem.objects.get_or_create( entry_id=ContainerEntry.id, entry_data={'label': _('Commercial')}, defaults={'order': 30}, )[0] MenuConfigItem.objects.create( entry_id=menu.OpportunitiesEntry.id, order=10, parent=container, ) creations = MenuConfigItem.objects.filter( entry_id=ContainerEntry.id, entry_data={ 'label': _('+ Creation') }, ).first() if creations is not None: MenuConfigItem.objects.create( entry_id=menu.OpportunityCreationEntry.id, order=30, parent=creations, ) # --------------------------- if not already_populated: create_sphase = SalesPhase.objects.create create_sphase(order=1, name=_('Forthcoming')) create_sphase(order=4, name=pgettext('opportunities-sales_phase', 'Abandoned')) create_sphase(order=5, name=pgettext('opportunities-sales_phase', 'Won'), won=True) create_sphase(order=6, name=pgettext('opportunities-sales_phase', 'Lost'), lost=True) create_sphase(order=3, name=_('Under negotiation')) create_sphase(order=2, name=_('In progress')) # --------------------------- create_origin = Origin.objects.create create_origin(name=pgettext('opportunities-origin', 'None')) create_origin(name=_('Web site')) create_origin(name=_('Mouth')) create_origin(name=_('Show')) create_origin(name=_('Direct email')) create_origin(name=_('Direct phone call')) create_origin(name=_('Employee')) create_origin(name=_('Partner')) create_origin(name=pgettext('opportunities-origin', 'Other')) # --------------------------- create_button = ButtonMenuItem.objects.create_if_needed create_button( model=Organisation, button=LinkedOpportunityButton, order=30, ) create_button( model=Contact, button=LinkedOpportunityButton, order=30, ) # --------------------------- LEFT = BrickDetailviewLocation.LEFT RIGHT = BrickDetailviewLocation.RIGHT build_cell = EntityCellRegularField.build cbci = CustomBrickConfigItem.objects.create( id='opportunities-complementary', name=_('Opportunity complementary information'), content_type=Opportunity, cells=[ build_cell(Opportunity, 'reference'), build_cell(Opportunity, 'currency'), build_cell(Opportunity, 'chance_to_win'), build_cell(Opportunity, 'expected_closing_date'), build_cell(Opportunity, 'closing_date'), build_cell(Opportunity, 'origin'), build_cell(Opportunity, 'first_action_date'), build_cell(Opportunity, 'description'), ], ) BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Opportunity, 'zone': LEFT }, data=[ { 'brick': bricks.OpportunityCardHatBrick, 'order': 1, 'zone': BrickDetailviewLocation.HAT, }, { 'brick': cbci.brick_id, 'order': 5 }, { 'brick': core_bricks.CustomFieldsBrick, 'order': 40 }, { 'brick': bricks.BusinessManagersBrick, 'order': 60 }, { 'brick': bricks.LinkedContactsBrick, 'order': 62 }, { 'brick': bricks.LinkedProductsBrick, 'order': 64 }, { 'brick': bricks.LinkedServicesBrick, 'order': 66 }, { 'brick': core_bricks.PropertiesBrick, 'order': 450 }, { 'brick': core_bricks.RelationsBrick, 'order': 500 }, { 'brick': bricks.OppTargetBrick, 'order': 1, 'zone': RIGHT }, { 'brick': bricks.OppTotalBrick, 'order': 2, 'zone': RIGHT }, { 'brick': core_bricks.HistoryBrick, 'order': 20, 'zone': RIGHT }, ], ) if apps.is_installed('creme.activities'): logger.info( 'Activities app is installed' ' => we use the "Future activities" & "Past activities" blocks' ) from creme.activities import bricks as act_bricks BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Opportunity, 'zone': RIGHT }, data=[ { 'brick': act_bricks.FutureActivitiesBrick, 'order': 20 }, { 'brick': act_bricks.PastActivitiesBrick, 'order': 21 }, ], ) if apps.is_installed('creme.assistants'): logger.info( 'Assistants app is installed' ' => we use the assistants blocks on detail views and portal' ) from creme.assistants import bricks as a_bricks BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Opportunity, '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': 500 }, ], ) if apps.is_installed('creme.documents'): # logger.info('Documents app is installed # => we use the documents block on detail view') from creme.documents.bricks import LinkedDocsBrick BrickDetailviewLocation.objects.create_if_needed( brick=LinkedDocsBrick, order=600, zone=RIGHT, model=Opportunity, ) if apps.is_installed('creme.billing'): logger.info('Billing app is installed' ' => we use the billing blocks on detail view') BrickDetailviewLocation.objects.multi_create( defaults={ 'model': Opportunity, 'zone': LEFT }, data=[ { 'brick': bricks.QuotesBrick, 'order': 70 }, { 'brick': bricks.SalesOrdersBrick, 'order': 72 }, { 'brick': bricks.InvoicesBrick, 'order': 74 }, ], ) if apps.is_installed('creme.emails'): logger.info('Emails app is installed' ' => we use the emails blocks on detail view') from creme.emails.bricks import MailsHistoryBrick BrickDetailviewLocation.objects.create_if_needed( brick=MailsHistoryBrick, order=600, zone=RIGHT, model=Opportunity, ) BrickDetailviewLocation.objects.create_if_needed( brick=bricks.TargettingOpportunitiesBrick, order=16, zone=RIGHT, model=Organisation, ) # --------------------------- if apps.is_installed('creme.reports'): logger.info( 'Reports app is installed' ' => we create an Opportunity report, with 2 graphs, and related blocks' ) self.create_report_n_graphes(rt_obj_emit_orga)
def populate(self): already_populated = RelationType.objects.filter( pk=constants.REL_SUB_TARGETS).exists() Contact = persons.get_contact_model() Organisation = persons.get_organisation_model() Product = products.get_product_model() Service = products.get_service_model() # --------------------------- create_rtype = RelationType.create rt_sub_targets = create_rtype( (constants.REL_SUB_TARGETS, _('targets the organisation/contact'), [Opportunity]), (constants.REL_OBJ_TARGETS, _('targeted by the opportunity'), [Organisation, Contact]), is_internal=True, minimal_display=(True, True), )[0] rt_obj_emit_orga = create_rtype( (constants.REL_SUB_EMIT_ORGA, _('has generated the opportunity'), [Organisation]), (constants.REL_OBJ_EMIT_ORGA, _('has been generated by'), [Opportunity]), is_internal=True, minimal_display=(True, True), )[1] create_rtype((constants.REL_SUB_LINKED_PRODUCT, _('is linked to the opportunity'), [Product]), (constants.REL_OBJ_LINKED_PRODUCT, _('concerns the product'), [Opportunity])) create_rtype((constants.REL_SUB_LINKED_SERVICE, _('is linked to the opportunity'), [Service]), (constants.REL_OBJ_LINKED_SERVICE, _('concerns the service'), [Opportunity])) create_rtype( (constants.REL_SUB_LINKED_CONTACT, _('involves in the opportunity'), [Contact]), (constants.REL_OBJ_LINKED_CONTACT, _('stages'), [Opportunity])) create_rtype((constants.REL_SUB_RESPONSIBLE, _('is responsible for'), [Contact]), (constants.REL_OBJ_RESPONSIBLE, _('has as responsible contact'), [Opportunity])) if apps.is_installed('creme.activities'): logger.info( 'Activities app is installed => an Opportunity 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(Opportunity) if apps.is_installed('creme.billing'): logger.info( 'Billing app is installed => we create relationships between Opportunities & billing models' ) from creme.billing import get_invoice_model, get_quote_model, get_sales_order_model Invoice = get_invoice_model() Quote = get_quote_model() SalesOrder = get_sales_order_model() create_rtype( (constants.REL_SUB_LINKED_SALESORDER, _('is associate with the opportunity'), [SalesOrder]), (constants.REL_OBJ_LINKED_SALESORDER, _('has generated the salesorder'), [Opportunity])) create_rtype((constants.REL_SUB_LINKED_INVOICE, _('generated for the opportunity'), [Invoice]), (constants.REL_OBJ_LINKED_INVOICE, _('has resulted in the invoice'), [Opportunity])) create_rtype((constants.REL_SUB_LINKED_QUOTE, _('generated for the opportunity'), [Quote]), (constants.REL_OBJ_LINKED_QUOTE, _('has resulted in the quote'), [Opportunity])) create_rtype( (constants.REL_SUB_CURRENT_DOC, _('is the current accounting document of'), [SalesOrder, Invoice, Quote]), (constants.REL_OBJ_CURRENT_DOC, _('has as current accounting document'), [Opportunity])) # --------------------------- create_sv = SettingValue.objects.get_or_create create_sv(key_id=setting_keys.quote_key.id, defaults={'value': False}) create_sv(key_id=setting_keys.target_constraint_key.id, defaults={'value': True}) create_sv(key_id=setting_keys.emitter_constraint_key.id, defaults={'value': True}) # --------------------------- create_efilter = partial(EntityFilter.create, model=Opportunity, user='******') # create_cond = partial(EntityFilterCondition.build_4_field, model=Opportunity) build_cond = partial( condition_handler.RegularFieldConditionHandler.build_condition, model=Opportunity, ) create_efilter( 'opportunities-opportunities_won', name=_('Opportunities won'), conditions=[ # create_cond(operator=EntityFilterCondition.EQUALS, # name='sales_phase__won', # values=[True], # ), build_cond( operator=operators.EqualsOperator, field_name='sales_phase__won', values=[True], ), ], ) create_efilter( 'opportunities-opportunities_lost', name=_('Opportunities lost'), conditions=[ # create_cond(operator=EntityFilterCondition.EQUALS, # name='sales_phase__lost', # values=[True], # ), build_cond( operator=operators.EqualsOperator, field_name='sales_phase__lost', values=[True], ), ], ) create_efilter( 'opportunities-neither_won_nor_lost_opportunities', name=_('Neither won nor lost opportunities'), conditions=[ # create_cond(operator=EntityFilterCondition.EQUALS_NOT, # name='sales_phase__won', # values=[True], # ), build_cond( operator=operators.EqualsNotOperator, field_name='sales_phase__won', values=[True], ), # create_cond(operator=EntityFilterCondition.EQUALS_NOT, # name='sales_phase__lost', # values=[True], # ), build_cond( operator=operators.EqualsNotOperator, field_name='sales_phase__lost', values=[True], ), ], ) # --------------------------- HeaderFilter.create( pk=constants.DEFAULT_HFILTER_OPPORTUNITY, model=Opportunity, name=_('Opportunity view'), cells_desc=[ (EntityCellRegularField, { 'name': 'name' }), EntityCellRelation(model=Opportunity, rtype=rt_sub_targets), (EntityCellRegularField, { 'name': 'sales_phase' }), (EntityCellRegularField, { 'name': 'estimated_sales' }), (EntityCellRegularField, { 'name': 'made_sales' }), (EntityCellRegularField, { 'name': 'closing_date' }), ], ) # --------------------------- SearchConfigItem.create_if_needed( Opportunity, ['name', 'made_sales', 'sales_phase__name', 'origin__name']) # --------------------------- if not already_populated: create_sphase = SalesPhase.objects.create create_sphase(name=_('Forthcoming'), order=1) create_sphase(name=pgettext('opportunities-sales_phase', 'Abandoned'), order=4) create_sphase(name=pgettext('opportunities-sales_phase', 'Won'), order=5, won=True) create_sphase(name=pgettext('opportunities-sales_phase', 'Lost'), order=6, lost=True) create_sphase(name=_('Under negotiation'), order=3) create_sphase(name=_('In progress'), order=2) # --------------------------- create_origin = Origin.objects.create create_origin(name=pgettext('opportunities-origin', 'None')) create_origin(name=_('Web site')) create_origin(name=_('Mouth')) create_origin(name=_('Show')) create_origin(name=_('Direct email')) create_origin(name=_('Direct phone call')) create_origin(name=_('Employee')) create_origin(name=_('Partner')) create_origin(name=pgettext('opportunities-origin', 'Other')) # --------------------------- create_button = ButtonMenuItem.create_if_needed create_button(pk='opportunities-linked_opp_button', model=Organisation, button=LinkedOpportunityButton, order=30) # TODO: This pk is kept for compatibility create_button(pk='opportunities-linked_opp_button_contact', model=Contact, button=LinkedOpportunityButton, order=30) # --------------------------- create_bdl = partial( BrickDetailviewLocation.objects.create_if_needed, model=Opportunity) LEFT = BrickDetailviewLocation.LEFT RIGHT = BrickDetailviewLocation.RIGHT build_cell = EntityCellRegularField.build cbci = CustomBrickConfigItem.objects.create( id='opportunities-complementary', name=_('Opportunity complementary information'), content_type=Opportunity, cells=[ build_cell(Opportunity, 'reference'), build_cell(Opportunity, 'currency'), build_cell(Opportunity, 'chance_to_win'), build_cell(Opportunity, 'expected_closing_date'), build_cell(Opportunity, 'closing_date'), build_cell(Opportunity, 'origin'), build_cell(Opportunity, 'first_action_date'), build_cell(Opportunity, 'description'), ], ) create_bdl(brick=bricks.OpportunityCardHatBrick, order=1, zone=BrickDetailviewLocation.HAT) create_bdl(brick=cbci.generate_id(), order=5, zone=LEFT) create_bdl(brick=core_bricks.CustomFieldsBrick, order=40, zone=LEFT) create_bdl(brick=bricks.BusinessManagersBrick, order=60, zone=LEFT) create_bdl(brick=bricks.LinkedContactsBrick, order=62, zone=LEFT) create_bdl(brick=bricks.LinkedProductsBrick, order=64, zone=LEFT) create_bdl(brick=bricks.LinkedServicesBrick, order=66, zone=LEFT) create_bdl(brick=core_bricks.PropertiesBrick, order=450, zone=LEFT) create_bdl(brick=core_bricks.RelationsBrick, order=500, zone=LEFT) create_bdl(brick=bricks.OppTargetBrick, order=1, zone=RIGHT) create_bdl(brick=bricks.OppTotalBrick, order=2, zone=RIGHT) create_bdl(brick=core_bricks.HistoryBrick, order=20, zone=RIGHT) if apps.is_installed('creme.activities'): logger.info( 'Activities app is installed => we use the "Future activities" & "Past activities" blocks' ) from creme.activities import bricks as act_bricks create_bdl(brick=act_bricks.FutureActivitiesBrick, order=20, zone=RIGHT) create_bdl(brick=act_bricks.PastActivitiesBrick, order=21, zone=RIGHT) if apps.is_installed('creme.assistants'): logger.info( 'Assistants app is installed => we use the assistants blocks on detail views and portal' ) from creme.assistants import bricks as assistants_bricks create_bdl(brick=assistants_bricks.TodosBrick, order=100, zone=RIGHT) create_bdl(brick=assistants_bricks.MemosBrick, order=200, zone=RIGHT) create_bdl(brick=assistants_bricks.AlertsBrick, order=300, zone=RIGHT) create_bdl(brick=assistants_bricks.UserMessagesBrick, order=500, zone=RIGHT) if apps.is_installed('creme.documents'): # logger.info('Documents app is installed => we use the documents block on detail view') from creme.documents.bricks import LinkedDocsBrick create_bdl(brick=LinkedDocsBrick, order=600, zone=RIGHT) if apps.is_installed('creme.billing'): logger.info( 'Billing app is installed => we use the billing blocks on detail view' ) create_bdl(brick=bricks.QuotesBrick, order=70, zone=LEFT) create_bdl(brick=bricks.SalesOrdersBrick, order=72, zone=LEFT) create_bdl(brick=bricks.InvoicesBrick, order=74, zone=LEFT) if apps.is_installed('creme.emails'): logger.info( 'Emails app is installed => we use the emails blocks on detail view' ) from creme.emails.bricks import MailsHistoryBrick create_bdl(brick=MailsHistoryBrick, order=600, zone=RIGHT) create_bdl(brick=bricks.TargettingOpportunitiesBrick, order=16, zone=RIGHT, model=Organisation) # --------------------------- if apps.is_installed('creme.reports'): logger.info( 'Reports app is installed => we create an Opportunity report, with 2 graphs, and related blocks' ) self.create_report_n_graphes(rt_obj_emit_orga)