def customize_school(): dd.inject_field('courses.Pupil', 'school', models.ForeignKey(School, blank=True, null=True, help_text=_("""The regular school where this child goes.""") ))
def inject_pricefactor_fields(sender, **kw): for pf in PriceFactors.get_list_items(): dd.inject_field( 'products.PriceRule', pf.field_name, pf.field_cls.field(blank=True)) dd.inject_field( 'contacts.Partner', pf.field_name, pf.field_cls.field(blank=True))
def inject_summary_fields(sender, **kw): SiteSummary = rt.models.working.SiteSummary WorkSite = rt.models.tickets.Site Ticket = dd.plugins.working.ticket_model for t in ReportingTypes.get_list_items(): k = t.name + '_hours' dd.inject_field( SiteSummary, k, dd.DurationField(t.text, null=True, blank=True)) dd.inject_field( Ticket, k, dd.DurationField(t.text, null=True, blank=True)) def make_getter(t): k = t.name + '_hours' def getter(obj, ar): qs = SiteSummary.objects.filter( master=obj, year__isnull=True) d = qs.aggregate(**{k:models.Sum(k)}) n = d[k] return n return getter dd.inject_field( WorkSite, k, dd.VirtualField( dd.DurationField(t.text), make_getter(t))) if False: # removed 20181211 because useless for ts in TicketStates.get_list_items(): k = ts.get_summary_field() if k is not None: dd.inject_field( SiteSummary, k, models.IntegerField(ts.text)) def make_getter(ts): k = ts.get_summary_field() def getter(obj, ar): if ar is None: return '' qs = SiteSummary.objects.filter(master=obj) d = qs.aggregate(**{k:models.Sum(k)}) n = d[k] if n == 0: return '' sar = rt.models.tickets.TicketsBySite.request( obj, param_values=dict( state=ts, show_active=None)) # n = sar.get_total_count() url = ar.renderer.request_handler(sar) if url is None: return str(n) return E.a(str(n), href='javascript:'+url) return getter dd.inject_field( WorkSite, k, dd.VirtualField( dd.DisplayField(ts.text), make_getter(ts)))
def customize_contacts(sender, **kw): """ Injects application-specific fields to :mod:`contacts <lino_xl.lib.contacts>`. """ dd.inject_field( contacts.RoleType, 'use_in_contracts', models.BooleanField( verbose_name=_("usable in contracts"), default=True, help_text=_( "Whether Links of this type can be used " "as contact person of a job contract.")))
def customize_siteconfig(): """ Injects application-specific fields to :class:`SiteConfig <lino.modlib.system.SiteConfig>`. """ dd.inject_field( 'system.SiteConfig', 'system_note_type', dd.ForeignKey( 'notes.EventType', blank=True, null=True, verbose_name=_("Default system note type"), help_text=_("""\ Note Type used by system notes. If this is empty, then system notes won't create any entry to the Notes table.""")))
def customize_siteconfig(): """ Injects application-specific fields to :class:`SiteConfig <lino.modlib.system.SiteConfig>`. """ dd.inject_field( 'system.SiteConfig', 'system_note_type', dd.ForeignKey('notes.EventType', blank=True, null=True, verbose_name=_("Default system note type"), help_text=_("""\ Note Type used by system notes. If this is empty, then system notes won't create any entry to the Notes table.""" )))
def inject_partner_field(sender=None, **kwargs): User = sender.models.users.User if dd.is_installed('contacts'): Partner = sender.models.contacts.Partner if not issubclass(User, Partner): dd.inject_field(User, 'partner', dd.ForeignKey( 'contacts.Partner', blank=True, null=True, related_name='users_by_partner', on_delete=models.PROTECT)) # a related_name is needed so that Avanti can have aClient # who inherits from both Partner and UserAuthored return dd.inject_field(User, 'partner', dd.DummyField())
def inject_tradetype_fields(sender, **kw): """This defines certain database fields related to your :class:`TradeTypes`. """ for tt in TradeTypes.items(): if tt.partner_account_field_name is not None: dd.inject_field( 'system.SiteConfig', tt.partner_account_field_name, dd.ForeignKey('accounts.Account', verbose_name=tt.partner_account_field_label, related_name='configs_by_' + tt.partner_account_field_name, blank=True, null=True)) if tt.vat_account_field_name is not None: dd.inject_field( 'system.SiteConfig', tt.vat_account_field_name, dd.ForeignKey('accounts.Account', verbose_name=tt.vat_account_field_label, related_name='configs_by_' + tt.vat_account_field_name, blank=True, null=True)) if tt.base_account_field_name is not None: dd.inject_field( 'system.SiteConfig', tt.base_account_field_name, dd.ForeignKey('accounts.Account', verbose_name=tt.base_account_field_label, related_name='configs_by_' + tt.base_account_field_name, blank=True, null=True)) dd.inject_field( 'products.Product', tt.base_account_field_name, dd.ForeignKey('accounts.Account', verbose_name=tt.base_account_field_label, related_name='products_by_' + tt.base_account_field_name, blank=True, null=True)) if tt.price_field_name is not None: dd.inject_field( 'products.Product', tt.price_field_name, dd.PriceField(verbose_name=tt.price_field_label, blank=True, null=True))
def inject_partner_field(sender=None, **kwargs): User = sender.models.users.User if dd.is_installed('contacts'): Partner = sender.models.contacts.Partner if not issubclass(User, Partner): dd.inject_field( User, 'partner', dd.ForeignKey('contacts.Partner', blank=True, null=True, related_name='users_by_partner', on_delete=models.PROTECT)) # a related_name is needed so that Avanti can have aClient # who inherits from both Partner and UserAuthored return dd.inject_field(User, 'partner', dd.DummyField())
def inject_tradetype_fields(sender, **kw): """This defines certain database fields related to your :class:`TradeTypes`. """ for tt in TradeTypes.items(): if tt.partner_account_field_name is not None: dd.inject_field( 'system.SiteConfig', tt.partner_account_field_name, dd.ForeignKey( 'accounts.Account', verbose_name=tt.partner_account_field_label, related_name='configs_by_' + tt.partner_account_field_name, blank=True, null=True)) if tt.vat_account_field_name is not None: dd.inject_field('system.SiteConfig', tt.vat_account_field_name, dd.ForeignKey( 'accounts.Account', verbose_name=tt.vat_account_field_label, related_name='configs_by_' + tt.vat_account_field_name, blank=True, null=True)) if tt.base_account_field_name is not None: dd.inject_field('system.SiteConfig', tt.base_account_field_name, dd.ForeignKey( 'accounts.Account', verbose_name=tt.base_account_field_label, related_name='configs_by_' + tt.base_account_field_name, blank=True, null=True)) dd.inject_field('products.Product', tt.base_account_field_name, dd.ForeignKey( 'accounts.Account', verbose_name=tt.base_account_field_label, related_name='products_by_' + tt.base_account_field_name, blank=True, null=True)) if tt.price_field_name is not None: dd.inject_field('products.Product', tt.price_field_name, dd.PriceField(verbose_name=tt.price_field_label, blank=True, null=True))
def inject_tradetype_fields(sender, **kw): """This defines certain database fields related to your :class:`TradeTypes`. """ # print(20200622, list([i.invoice_account_field_name for i in TradeTypes.items()])) for tt in TradeTypes.items(): if tt.invoice_account_field_name is not None: dd.inject_field( 'contacts.Partner', tt.invoice_account_field_name, dd.ForeignKey('ledger.Account', verbose_name=tt.invoice_account_field_label, on_delete=models.PROTECT, related_name='partners_by_' + tt.invoice_account_field_name, blank=True, null=True)) if tt.base_account_field_name is not None: dd.inject_field( 'products.Product', tt.base_account_field_name, dd.ForeignKey('ledger.Account', verbose_name=tt.base_account_field_label, on_delete=models.PROTECT, related_name='products_by_' + tt.base_account_field_name, blank=True, null=True)) if tt.price_field_name is not None: dd.inject_field( 'products.Product', tt.price_field_name, dd.PriceField(verbose_name=tt.price_field_label, blank=True, null=True))
def customize_siteconfig(): dd.inject_field( 'system.SiteConfig', 'propgroup_skills', models.ForeignKey( 'properties.PropGroup', blank=True, null=True, verbose_name=_("Skills Property Group"), related_name='skills_sites', help_text=_( "The property group to be used as master " "for the SkillsByPerson table."))) dd.inject_field( 'system.SiteConfig', 'propgroup_softskills', models.ForeignKey( 'properties.PropGroup', blank=True, null=True, verbose_name=_("Soft Skills Property Group"), related_name='softskills_sites', help_text=_( "The property group to be used as master " "for the SoftSkillsByPerson table."))) dd.inject_field( 'system.SiteConfig', 'propgroup_obstacles', models.ForeignKey( 'properties.PropGroup', blank=True, null=True, verbose_name=_("Obstacles Property Group"), related_name='obstacles_sites', help_text=_( "The property group to be used as master " "for the ObstaclesByPerson table.")))
def inject_tradetype_fields(sender, **kw): """This defines certain database fields related to your :class:`TradeTypes`. """ for tt in TradeTypes.items(): if tt.invoice_account_field_name is not None: dd.inject_field( 'contacts.Partner', tt.invoice_account_field_name, dd.ForeignKey( 'ledger.Account', verbose_name=tt.invoice_account_field_label, on_delete=models.PROTECT, related_name='partners_by_' + tt.invoice_account_field_name, blank=True, null=True)) if tt.base_account_field_name is not None: dd.inject_field( 'products.Product', tt.base_account_field_name, dd.ForeignKey( 'ledger.Account', verbose_name=tt.base_account_field_label, on_delete=models.PROTECT, related_name='products_by_' + tt.base_account_field_name, blank=True, null=True)) if tt.price_field_name is not None: dd.inject_field( 'products.Product', tt.price_field_name, dd.PriceField(verbose_name=tt.price_field_label, blank=True, null=True))
def customize_siteconfig(sender, **kw): """ Injects application-specific fields to :class:`SiteConfig <lino.models.SiteConfig>`. """ dd.inject_field('system.SiteConfig', 'job_office', models.ForeignKey('contacts.Company', blank=True, null=True, verbose_name=_("Local job office"), related_name='job_office_sites', help_text="""The Company whose contact persons will be choices for `Person.job_office_contact`.""")) dd.inject_field('system.SiteConfig', 'residence_permit_upload_type', models.ForeignKey("uploads.UploadType", blank=True, null=True, verbose_name=_( "Upload Type for residence permit"), related_name='residence_permit_sites')) dd.inject_field('system.SiteConfig', 'work_permit_upload_type', #~ UploadType.objects.get(pk=2) models.ForeignKey("uploads.UploadType", blank=True, null=True, verbose_name=_( "Upload Type for work permit"), related_name='work_permit_sites')) dd.inject_field('system.SiteConfig', 'driving_licence_upload_type', models.ForeignKey("uploads.UploadType", blank=True, null=True, verbose_name=_( "Upload Type for driving licence"), related_name='driving_licence_sites'))
settings.SITE.add_user_field('newcomer_quota', models.IntegerField( _("Newcomers Quota"), default=0, help_text="""\ Wieviel Arbeitszeit dieser Benutzer für Neuanträge zur Verfügung steht (100 = ganztags, 50 = halbtags, 0 = gar nicht). Wenn zwei Benutzer die gleiche Belastungspunktzahl haben, aber einer davon sich nur zu 50% um Neuanträge kümmert, gilt er als doppelt so belastet wie sein Kollege. """)) dd.inject_field( 'pcsw.Client', 'broker', dd.ForeignKey( 'newcomers.Broker', blank=True, null=True, help_text=_("The Broker who sent this Newcomer."))) dd.inject_field( 'pcsw.Client', 'faculty', dd.ForeignKey( 'newcomers.Faculty', blank=True, null=True, help_text=_("The Faculty this client has been attributed to.")), active=True)
class NotesByProject(NotesByX): master_key = 'project' # stay_in_grid = True class NotesByOwner(NotesByX): master_key = 'owner' class NotesByCompany(NotesByX): master_key = 'company' class NotesByPerson(NotesByX): master_key = 'contact_person' dd.inject_field( 'system.SiteConfig', 'system_note_type', dd.ForeignKey( 'notes.EventType', blank=True, null=True, verbose_name=_("Default system note type"), help_text=_("""\ Note Type used by system notes. If this is empty, then system notes won't create any entry to the Notes table.""")))
title = models.CharField(_("Description"), max_length=200, blank=True) if False: """Install a post_init signal listener for each concrete subclass of VatDocument. The following trick worked... but best is to store it in VatRegime, not per voucher. """ def set_default_item_vat(sender, instance=None, **kwargs): instance.item_vat = settings.SITE.get_item_vat(instance) # print("20130902 set_default_item_vat", instance) @dd.receiver(dd.post_analyze) def on_post_analyze(sender, **kw): for m in rt.models_by_base(VatDocument): dd.post_init.connect(set_default_item_vat, sender=m) # print('20130902 on_post_analyze installed receiver for',m) dd.inject_field( 'contacts.Partner', 'vat_regime', VatRegimes.field(blank=True, help_text=_("The default VAT regime for \ sales and purchases of this partner."))) dd.inject_field('contacts.Company', 'vat_id', models.CharField(_("VAT id"), max_length=200, blank=True)) from .ui import *
def set_excerpts_actions(sender, **kw): # logger.info("20180114 %s.set_excerpts_actions()", __name__) # in case ExcerptType is overridden ExcerptType = sender.modules.excerpts.ExcerptType Excerpt = sender.modules.excerpts.Excerpt try: etypes = [(obj, obj.content_type) for obj in ExcerptType.objects.all().order_by('id')] except (OperationalError, ProgrammingError, UnresolvedChoice) as e: dd.logger.debug("Failed to set excerpts actions : %s", e) # Happens e.g. when the database has not yet been migrated etypes = [] for atype, ct in etypes: if ct is not None: bm = ct.model_class() if bm is None: # e.g. database contains types for models that existed # before but have been removed continue for m in rt.models_by_base(bm): an = atype.get_action_name() if not hasattr(m, an): m.define_action(**{an: CreateExcerpt( atype, six.text_type(atype))}) # dd.logger.info("Added print action to %s", m) # if atype.certifying and not issubclass(m, Certifiable): # m.define_action( # clear_printed=ClearCache()) # An attestable model must also inherit # :class:`lino.mixins.printable.BasePrintable` or some subclass # thereof. for i in Shortcuts.items(): def f(obj, ar): if ar is None: return '' if obj is None: return E.div() try: et = ExcerptType.objects.get(shortcut=i) except ExcerptType.DoesNotExist: return E.div() items = [] if True: sar = ar.spawn( ExcerptsByOwner, master_instance=obj, param_values=dict(excerpt_type=et)) n = sar.get_total_count() if n > 0: ex = sar.sliced_data_iterator[0] items.append(ar.obj2html(ex, _("Last"))) ba = sar.bound_action btn = sar.renderer.action_button( obj, sar, ba, "%s (%d)" % (_("All"), n), icon_name=None) items.append(btn) ia = getattr(obj, et.get_action_name()) btn = ar.instance_action_button( ia, _("Create"), icon_name=None) items.append(btn) else: ot = ContentType.objects.get_for_model(obj.__class__) qs = Excerpt.objects.filter( owner_id=obj.pk, owner_type=ot, excerpt_type=et) if qs.count() > 0: ex = qs[0] txt = ExcerptsByOwner.format_excerpt(ex) items.append(ar.obj2html(ex, txt)) return E.div(*join_elems(items, ', ')) vf = dd.VirtualField(dd.DisplayField(i.text), f) dd.inject_field(i.model_spec, i.name, vf)
model = User column_names = 'username person company is_superuser is_staff *' if settings.SITE.is_installed('contacts'): """ Don't inject fields if contacts is just being imported from some other module. """ from lino.models import SiteConfig dd.inject_field( SiteConfig, 'site_company', models.ForeignKey( Company, blank=True, null=True, verbose_name=_("The company that runs this site"), related_name='site_company_sites', ), """The Company to be used as sender in documents.""") NAME = "contacts" LABEL = _("Contacts") def setup_main_menu(config, site, profile, m): m.add_action(Companies) m.add_action(Persons) #~ m.add_action(Contacts)
# # def preview(obj, ar): return obj.html or obj.text def spam(obj): """Checks if the message is spam or not """ if obj.subject.startswith("*****SPAM*****"): return True else: return False dd.inject_field('django_mailbox.Message', 'preview', dd.VirtualField(dd.HtmlBox(_("Preview")), preview)) dd.inject_field('django_mailbox.Message', 'ticket', dd.ForeignKey('tickets.Ticket', blank=True, null=True)) dd.update_field('django_mailbox.Message', 'from_header', format="plain") from .ui import * @dd.schedule_often(10) def get_new_mail(): for mb in rt.models.django_mailbox.Mailbox.objects.filter(active=True): mails = mb.get_new_mail() for mail in mails: if spam(mail): mail.spam = True
def set_excerpts_actions(sender, **kw): """Installs (1) print management actions on models for which there is an excerpt type and (2) the excerpt shortcut fields defined in :class:`lino.modlib.excerpts.choicelists.Shortcuts`. """ # logger.info("20140401 %s.set_attest_actions()", __name__) # in case ExcerptType is overridden ExcerptType = sender.modules.excerpts.ExcerptType Excerpt = sender.modules.excerpts.Excerpt try: etypes = [(obj, obj.content_type) for obj in ExcerptType.objects.all()] except (OperationalError, ProgrammingError): # logger.warning("Failed to set excerpts actions : %s", e) # Happens e.g. when the database has not yet been migrated etypes = [] for atype, ct in etypes: if ct is not None: m = ct.model_class() if m is not None: # e.g. database contains types for # models that existed before but have # been removed an = atype.get_action_name() m.define_action(**{an: CreateExcerpt(atype, unicode(atype))}) # if atype.primary: # if atype.certifying: # m.define_action( # clear_printed=ClearPrinted()) # An attestable model must also inherit # :class:`lino.mixins.printable.BasePrintable` or some subclass # thereof. for i in Shortcuts.items(): def f(obj, ar): if ar is None: return '' if obj is None: return E.div() try: et = ExcerptType.objects.get(shortcut=i) except ExcerptType.DoesNotExist: return E.div() items = [] if True: sar = ar.spawn( ExcerptsByOwner, master_instance=obj, param_values=dict(excerpt_type=et)) n = sar.get_total_count() if n > 0: ex = sar.sliced_data_iterator[0] items.append(ar.obj2html(ex, _("Last"))) ba = sar.bound_action btn = sar.renderer.action_button( obj, sar, ba, "%s (%d)" % (_("All"), n), icon_name=None) items.append(btn) ia = getattr(obj, et.get_action_name()) btn = ar.instance_action_button( ia, _("Create"), icon_name=None) items.append(btn) else: ot = ContentType.objects.get_for_model(obj.__class__) qs = Excerpt.objects.filter( owner_id=obj.pk, owner_type=ot, excerpt_type=et) if qs.count() > 0: ex = qs[0] txt = ExcerptsByOwner.format_excerpt(ex) items.append(ar.obj2html(ex, txt)) return E.div(*join_elems(items, ', ')) vf = dd.VirtualField(dd.DisplayField(i.text), f) dd.inject_field(i.model_spec, i.name, vf)
class RolesByPerson(Roles): """Shows all roles of a person.""" required_roles = dd.login_required(SimpleContactsUser) #~ required_user_level = None label = _("Contact for") master_key = 'person' column_names = 'company type start_date end_date *' auto_fit_column_widths = True hidden_columns = 'id' dd.inject_field( 'system.SiteConfig', 'next_partner_id', models.IntegerField( default=PARTNER_NUMBERS_START_AT, blank=True, null=True, verbose_name=_("Next partner id"), help_text=_("The next automatic id for any new partner."))) @dd.receiver(dd.pre_analyze) def company_model_alias(sender, **kw): """ prepare ticket #72 which will rename Company to Organisation """ sender.modules.contacts.Organisation = sender.modules.contacts.Company @dd.receiver(dd.post_analyze)
master_key = 'project' class NotesByOwner(NotesByX): master_key = 'owner' column_names = "date time event_type type subject user *" class NotesByCompany(NotesByX): master_key = 'company' column_names = "date time event_type type subject user *" class NotesByPerson(NotesByX): master_key = 'contact_person' column_names = "date time event_type type subject user *" # system = dd.resolve_app('system') dd.inject_field( 'system.SiteConfig', 'system_note_type', dd.ForeignKey('notes.EventType', blank=True, null=True, verbose_name=_("Default system note type"), help_text=_("""\ Note Type used by system notes. If this is empty, then system notes won't create any entry to the Notes table.""" )))
""" .. attribute:: invite_client .. attribute:: esf_field """ #~ invite_team_members = models.BooleanField( #~ _("Invite team members"),default=False) # invite_team_members = dd.ForeignKey('users.Team', blank=True, null=True) invite_client = models.BooleanField(_("Invite client"), default=False) esf_field = dd.DummyField() dd.inject_field( 'users.User', 'calendar', dd.ForeignKey( 'cal.Calendar', verbose_name=_("User Calendar"), help_text=_("Calendar where your events are published."), related_name='user_calendars', blank=True, null=True)) dd.inject_field( 'system.SiteConfig', 'client_calendar', dd.ForeignKey( 'cal.EventType', verbose_name=_("Default type for client calendar events"), related_name='client_calendars', blank=True, null=True)) dd.inject_field( 'system.SiteConfig', 'client_guestrole', dd.ForeignKey(
def inject_summary_fields(sender, **kw): SiteSummary = rt.models.working.SiteSummary UserSummary = rt.models.working.UserSummary WorkSite = rt.models.tickets.Site Ticket = dd.plugins.working.ticket_model for t in ReportingTypes.get_list_items(): k = t.name + '_hours' dd.inject_field(SiteSummary, k, dd.DurationField(t.text, null=True, blank=True)) dd.inject_field(UserSummary, k, dd.DurationField(t.text, null=True, blank=True)) dd.inject_field(Ticket, k, dd.DurationField(t.text, null=True, blank=True)) def make_getter(t): k = t.name + '_hours' def getter(obj, ar): qs = SiteSummary.objects.filter(master=obj, year__isnull=True) d = qs.aggregate(**{k: models.Sum(k)}) n = d[k] return n return getter dd.inject_field( WorkSite, k, dd.VirtualField(dd.DurationField(t.text), make_getter(t))) if False: # removed 20181211 because useless for ts in TicketStates.get_list_items(): k = ts.get_summary_field() if k is not None: dd.inject_field(SiteSummary, k, models.IntegerField(ts.text)) def make_getter(ts): k = ts.get_summary_field() def getter(obj, ar): if ar is None: return '' qs = SiteSummary.objects.filter(master=obj) d = qs.aggregate(**{k: models.Sum(k)}) n = d[k] if n == 0: return '' sar = rt.models.tickets.TicketsBySite.request( obj, param_values=dict(state=ts, show_active=None)) # n = sar.get_total_count() url = ar.renderer.request_handler(sar) if url is None: return str(n) return E.a(str(n), href='javascript:' + url) return getter dd.inject_field( WorkSite, k, dd.VirtualField(dd.DisplayField(ts.text), make_getter(ts)))
#~ def setup_handle(self,lh): #~ lh.config.label = _("Site Parameters") #~ lh.about.label = _("About") class SiteConfigs(dd.Table): """ The table used to present the :class:`SiteConfig` row in a Detail form. See also :meth:`lino.Lino.get_site_config`. Deserves more documentation. """ model = 'system.SiteConfig' required_roles = dd.required(SiteStaff) default_action = actions.ShowDetailAction() #~ has_navigator = False hide_top_toolbar = True #~ can_delete = perms.never detail_layout = """ default_build_method # lino.ModelsBySite """ do_build = BuildSiteCache() if settings.SITE.user_model == 'auth.User': dd.inject_field(settings.SITE.user_model, 'profile', UserProfiles.field()) dd.inject_field(settings.SITE.user_model, 'language', dd.LanguageField())
# will clear GuestStates import lino.modlib.cal.workflows.feedback add = GuestStates.add_item add('44', _("Waiting"), 'waiting') add('45', _("Busy"), 'busy') add('46', _("Gone"), 'gone') #~ add = GuestStates.add_item #~ add('21', _("Waiting"),'waiting') dd.inject_field( 'cal.Guest', 'waiting_since', models.DateTimeField( _("Waiting since"), editable=False, blank=True, null=True, help_text=_("Time when the visitor arrived (checked in)."))) dd.inject_field( 'cal.Guest', 'busy_since', models.DateTimeField( _("Waiting until"), editable=False, blank=True, null=True, help_text=_("Time when the visitor was received by agent."))) dd.inject_field( 'cal.Guest', 'gone_since', models.DateTimeField( _("Present until"), editable=False, blank=True, null=True, help_text=_("Time when the visitor left (checked out).")))
abstract = dd.is_abstract_model(__name__, 'ClientContact') #~ type = ClientContactTypes.field(blank=True) client = dd.ForeignKey(client_model) remark = models.TextField(_("Remarks"), blank=True) # ,null=True) # allow_cascaded_delete = 'client' def full_clean(self, *args, **kw): if not self.remark and not self.type \ and not self.company and not self.contact_person: raise ValidationError(_("Must fill at least one field.")) super(ClientContact, self).full_clean(*args, **kw) dd.update_field(ClientContact, 'contact_person', verbose_name=_("Contact person")) dd.inject_field( 'contacts.Partner', 'client_contact_type', dd.ForeignKey( 'clients.ClientContactType', blank=True, null=True)) from lino_xl.lib.contacts.models import Partners class PartnersByClientContactType(Partners): master_key = 'client_contact_type' column_names = "name address_column phone gsm email *" auto_fit_column_widths = True
class RolesByPerson(Roles): required_roles = dd.required(ContactsUser) #~ required_user_level = None label = _("Contact for") master_key = 'person' column_names = 'company type *' auto_fit_column_widths = True hidden_columns = 'id' dd.inject_field( 'system.SiteConfig', 'next_partner_id', models.IntegerField( default=PARTNER_NUMBERS_START_AT, blank=True, null=True, verbose_name=_("Next partner id"), help_text=_("The next automatic id for any new partner."))) dd.inject_field( 'system.SiteConfig', 'site_company', models.ForeignKey("contacts.Company", blank=True, null=True, verbose_name=_("Site owner"), related_name='site_company_sites', help_text=_("""The organisation who runs this site. This is used e.g. as sender in documents. Or, newly created partners inherit the country of the site owner. """)))
ses = qs.first() if ses is None: if obj.fixed_since is not None: if fix: obj.fixed_since = None obj.full_clean() obj.save() yield (True, _("No fixing session but marked as fixed")) else: if obj.fixed_since is None: if fix: obj.fixed_since = ses.get_datetime('end') obj.full_clean() obj.save() yield (True, _( "Fixing session exists but ticket not marked as fixed")) TicketSessionsChecker.activate() # dd.inject_field( # 'tickets.Project', # 'reporting_type', ReportingTypes.field(blank=True)) dd.inject_field( "users.User", 'open_session_on_new_ticket', models.BooleanField(_("Open session on new ticket"), default=False)) from .ui import *
# will clear GuestStates import lino.modlib.cal.workflows.feedback add = GuestStates.add_item add('44', _("Waiting"), 'waiting') add('45', _("Busy"), 'busy') add('46', _("Gone"), 'gone') #~ add = GuestStates.add_item #~ add('21', _("Waiting"),'waiting') dd.inject_field( 'cal.Guest', 'waiting_since', models.DateTimeField( _("Waiting since"), editable=False, blank=True, null=True, help_text=_("Time when the visitor arrived (checked in)."))) dd.inject_field( 'cal.Guest', 'busy_since', models.DateTimeField( _("Waiting until"), editable=False, blank=True, null=True, help_text=_("Time when the visitor was received by agent."))) dd.inject_field( 'cal.Guest', 'gone_since', models.DateTimeField( _("Present until"),
ses = qs.first() if ses is None: if obj.fixed_since is not None: if fix: obj.fixed_since = None obj.full_clean() obj.save() yield (True, _("No fixing session but marked as fixed")) else: if obj.fixed_since is None: if fix: obj.fixed_since = ses.get_datetime('end') obj.full_clean() obj.save() yield ( True, _("Fixing session exists but ticket not marked as fixed")) TicketSessionsChecker.activate() # dd.inject_field( # 'tickets.Project', # 'reporting_type', ReportingTypes.field(blank=True)) dd.inject_field( "users.User", 'open_session_on_new_ticket', models.BooleanField(_("Open session on new ticket"), default=False)) from .ui import *
update_auto_task( None, # REMINDER, obj.user, reminder_date, summary, obj, done=reminder_done, alarm_value=delay_value, alarm_unit=delay2alarm(delay_type)) # Inject application-specific fields to users.User. dd.inject_field(settings.SITE.user_model, 'access_class', AccessClasses.field( default=AccessClasses.public.as_callable, verbose_name=_("Default access class"), help_text=_( """The default access class for your calendar events and tasks.""") )) dd.inject_field(settings.SITE.user_model, 'event_type', models.ForeignKey('cal.EventType', blank=True, null=True, verbose_name=_("Default Event Type"), help_text=_("""The default event type for your calendar events.""") )) dd.inject_field( 'system.SiteConfig', 'default_event_type', models.ForeignKey(
max_length=100, blank=True, ) defaults.update(kw) models.CharField.__init__(self, *args, **defaults) def from_db_value(self, value, expression, connection, context): return CRL(hex2str(value)) if value else '' def to_python(self, value): if not value: return value if isinstance(value, CRL): return value return CRL(hex2str(value)) def get_prep_value(self, value): if not value: return value assert isinstance(value, CRL) return str2hex(value) dd.inject_field(countries.Place, 'crl', CrlField()) dd.inject_field(contacts.Person, 'crl', CrlField()) dd.inject_field(contacts.Person, 'died_date', models.DateField( blank=True, null=True, verbose_name=_("Died date"))) dd.inject_field(contacts.Company, 'crl', CrlField())
# of Django, not just inside a consumer. from channels import Group logger.info("Sending browser notification to %s", user.username) Group(groupname(user.username)).send({ # WebSocket text frame, with JSON content "text": json.dumps(message), }) return dd.update_field(Message, 'user', verbose_name=_("Recipient"), editable=False) # Message.update_controller_field( # null=True, blank=True, verbose_name=_("About")) dd.inject_field('users.User', 'notify_myself', models.BooleanField(_('Notify myself'), default=False)) dd.inject_field('users.User', 'mail_mode', MailModes.field(default=MailModes.as_callable('often'))) class Messages(dd.Table): model = 'notify.Message' column_names = "created subject user seen sent *" # cell_edit = False # detail_layout = dd.DetailLayout(""" # created user seen sent owner # overview # """, window_size=(50, 15))
class Accounts(dd.Table): required_roles = dd.login_required(SepaStaff) model = 'sepa.Account' class AccountsByPartner(Accounts): """Show the bank account(s) defined for a given partner. To be included to a detail window on partner. """ required_roles = dd.login_required((ContactsUser, SepaUser)) master_key = 'partner' column_names = 'iban bic remark primary *' order_by = ['iban'] stay_in_grid = True auto_fit_column_widths = True insert_layout = """ iban bic remark """ dd.inject_field( 'ledger.Journal', 'sepa_account', dd.ForeignKey('sepa.Account', blank=True, null=True, help_text=_("Your bank account to specify in payment order.")))
VoucherTypes.add_item_lazy(InvoicesByJournal) class PrintableInvoicesByJournal(PrintableByJournal, Invoices): label = _("Purchase journal (analytic)") class ItemsByInvoice(ItemsByVoucher): model = 'ana.InvoiceItem' column_names = "account title ana_account vat_class total_base total_vat total_incl *" display_mode = 'grid' dd.inject_field( 'ledger.Movement', 'ana_account', dd.ForeignKey('ana.Account', blank=True, null=True)) dd.inject_field( 'ledger.Account', 'ana_account', dd.ForeignKey('ana.Account', blank=True, null=True)) dd.inject_field( 'ledger.Account', 'needs_ana', models.BooleanField(_("Needs analytical account"), default=False)) # if dd.is_installed('vat'): # dd.inject_field( # 'vat.InvoiceItem', 'ana_account', # dd.ForeignKey('ana.Account', blank=True, null=True))
# Websocket. Note how you can send to a channel or Group from any part # of Django, not just inside a consumer. from channels import Group Group(user.username).send({ # WebSocket text frame, with JSON content "text": json.dumps(message), }) return dd.update_field(Message, 'user', verbose_name=_("Recipient"), editable=False) # Message.update_controller_field( # null=True, blank=True, verbose_name=_("About")) dd.inject_field('users.User', 'notify_myself', models.BooleanField(_('Notify myself'), default=False)) dd.inject_field( 'users.User', 'mail_mode', MailModes.field(_('Email notification mode'), default=MailModes.often.as_callable)) class Messages(dd.Table): "Base for all tables of messages." model = 'notify.Message' column_names = "created subject user seen sent *" # detail_layout = dd.DetailLayout(""" # created user seen sent owner # overview
it in VatRegime, not per voucher. """ def set_default_item_vat(sender, instance=None, **kwargs): instance.item_vat = settings.SITE.get_item_vat(instance) # print("20130902 set_default_item_vat", instance) @dd.receiver(dd.post_analyze) def on_post_analyze(sender, **kw): for m in rt.models_by_base(VatDocument): dd.post_init.connect(set_default_item_vat, sender=m) # print('20130902 on_post_analyze installed receiver for',m) dd.inject_field( 'contacts.Partner', 'vat_regime', VatRegimes.field(blank=True)) def get_vat_regime_choices(country=None, vat_id=None): vat_area = VatAreas.get_for_country(country) # print("20190405", vat_area) for r in VatRegimes.get_list_items(): if vat_area is None or r.vat_area is None or r.vat_area == vat_area: if vat_id or not r.needs_vat_id: yield r @dd.chooser() def partner_vat_regime_choices(cls, country, vat_id): return get_vat_regime_choices(country, vat_id)
# Copyright 2012-2019 Rumma & Ko Ltd # License: BSD (see file COPYING for details) from lino.api import dd, _ from lino_xl.lib.vat.mixins import VatDeclaration from .choicelists import DeclarationFields DEMO_JOURNAL_NAME = "VAT" # print("20170711a {}".format(DeclarationFields.get_list_items())) class Declaration(VatDeclaration): fields_list = DeclarationFields class Meta: app_label = 'eevat' verbose_name = _("Estonian VAT declaration") verbose_name_plural = _("Estonian VAT declarations") for fld in DeclarationFields.get_list_items(): dd.inject_field('eevat.Declaration', fld.name, fld.get_model_field())
fields_list = DeclarationFields class Meta: app_label = 'eevat' verbose_name = _("Estonian VAT declaration") verbose_name_plural = _("Estonian VAT declarations") # if dd.is_installed('declarations'): # avoid autodoc failure # # importing the country module will fill DeclarationFields # import_module(dd.plugins.declarations.country_module) from lino_xl.lib.vat.mixins import DECLARED_IN if DECLARED_IN: dd.inject_field('ledger.Voucher', 'declared_in', dd.ForeignKey(Declaration, blank=True, null=True)) # dd.inject_field('ledger.Account', # 'declaration_field', # DeclarationFields.field(blank=True, null=True)) # dd.inject_field('ledger.Journal', # 'declared', # models.BooleanField(default=True)) for fld in DeclarationFields.get_list_items(): dd.inject_field('eevat.Declaration', fld.name, fld.get_model_field())
# return str(ar) return tostring(e) sender.kernel.memo_parser.register_django_model( 'order', Order, cmd=cmd, # title=lambda obj: obj.name ) # class OrderItem(ProductDocItem, SequencedVoucherItem): class OrderItem(SequencedVoucherItem): class Meta: app_label = 'orders' abstract = dd.is_abstract_model(__name__, 'OrderItem') verbose_name = _("Order item") verbose_name_plural = _("Order items") allow_cascaded_delete = 'voucher' voucher = dd.ForeignKey('orders.Order', related_name='items') # title = models.CharField(_("Heading"), max_length=200, blank=True) product = dd.ForeignKey('products.Product', blank=True, null=True) qty = dd.QuantityField(_("Quantity"), blank=True, null=True) # unit_price = dd.PriceField(_("Unit price"), blank=True, null=True) remark = models.CharField(_("Remark"), max_length=200, blank=True) dd.inject_field('ledger.Journal', 'room', dd.ForeignKey('cal.Room', blank=True, null=True)) from .ui import *
def inject_subsidization_fields(sender, **kw): for sub in Subsidizations.items(): dd.inject_field( 'art61.Contract', sub.contract_field_name(), models.BooleanField(verbose_name=sub.text, default=False))
journal = JournalRef() @dd.chooser() def unused_account_choices(self, journal): # would be nice, but doesn't work because matchrules are # usually entered via MatchRulesByJournal where journal is # always None. if journal: fkw = {journal.trade_type.name + '_allowed': True} return rt.modules.accounts.Account.objects.filter(**fkw) print("20151221 journal is None") return [] for tt in TradeTypes.objects(): dd.inject_field('accounts.Account', tt.name + '_allowed', models.BooleanField(verbose_name=tt.text, default=False)) dd.inject_field( 'contacts.Partner', 'payment_term', models.ForeignKey('ledger.PaymentTerm', blank=True, null=True, help_text=_("The default payment term for " "sales invoices to this customer."))) class VoucherChecker(Checker): "Check for wrong ledger movements" verbose_name = _("Check integrity of ledger movements") messages = dict( missing=_("Missing movement {0}."),
def set_upload_shortcuts(sender, **kw): """This is the successor for `quick_upload_buttons`.""" # remember that models might have been overridden. UploadType = sender.modules.uploads.UploadType for i in Shortcuts.items(): def f(obj, ar): if obj is None or ar is None: return E.div() try: utype = UploadType.objects.get(shortcut=i) except UploadType.DoesNotExist: return E.div() items = [] target = sender.modules.resolve(i.target) sar = ar.spawn_request(actor=target, master_instance=obj, known_values=dict(type=utype)) # param_values=dict(pupload_type=et)) n = sar.get_total_count() if n == 0: iar = target.insert_action.request_from(sar, master_instance=obj) btn = iar.ar2button( None, _("Upload"), icon_name="page_add", title=_("Upload a file from your PC to the server.")) items.append(btn) elif n == 1: after_show = ar.get_status() obj = sar.data_iterator[0] items.append( sar.renderer.href_button(dd.build_media_url(obj.file.name), _("show"), target='_blank', icon_name='page_go', style="vertical-align:-30%;", title=_( "Open the uploaded file in a " "new browser window"))) after_show.update(record_id=obj.pk) items.append( sar.window_action_button( sar.ah.actor.detail_action, after_show, _("Edit"), icon_name='application_form', title=_("Edit metadata of the uploaded file."))) else: obj = sar.sliced_data_iterator[0] items.append( ar.obj2html(obj, pgettext("uploaded file", "Last"))) btn = sar.renderer.action_button(obj, sar, sar.bound_action, _("All {0} files").format(n), icon_name=None) items.append(btn) return E.div(*join_elems(items, ', ')) vf = dd.VirtualField(dd.DisplayField(i.text), f) dd.inject_field(i.model_spec, i.name, vf)
verbose_name=_("Label"), max_length=100, blank=True, ) defaults.update(kw) models.CharField.__init__(self, *args, **defaults) def from_db_value(self, value, expression, connection, context=None): return CRL(hex2str(value)) if value else '' def to_python(self, value): if not value: return value if isinstance(value, CRL): return value return CRL(hex2str(value)) def get_prep_value(self, value): if not value: return value assert isinstance(value, CRL) return str2hex(value) dd.inject_field(countries.Place, 'crl', CrlField()) dd.inject_field(contacts.Person, 'crl', CrlField()) dd.inject_field( contacts.Person, 'died_date', models.DateField(blank=True, null=True, verbose_name=_("Died date"))) dd.inject_field(contacts.Company, 'crl', CrlField())
update_auto_task( None, # REMINDER, obj.user, reminder_date, summary, obj, done=reminder_done, alarm_value=delay_value, alarm_unit=delay2alarm(delay_type)) # Inject application-specific fields to users.User. dd.inject_field(settings.SITE.user_model, 'access_class', AccessClasses.field( default=AccessClasses.as_callable('public'), verbose_name=_("Default access class"), help_text=_( """The default access class for your calendar events and tasks.""") )) dd.inject_field(settings.SITE.user_model, 'event_type', dd.ForeignKey('cal.EventType', blank=True, null=True, verbose_name=_("Default Event Type"), help_text=_("""The default event type for your calendar events.""") )) dd.inject_field( 'system.SiteConfig', 'default_event_type', dd.ForeignKey(
adapt the `actor` of this Entry by making it an actor of the new Budget. TODO: this method relies on the fact that related Actors get duplicated *before* related Entries. The order of `fklist` in `_lino_ddh` """ if master is not None and self.actor is not None and self.actor.budget != master: self.actor = master.actor_set.get(seqno=self.actor.seqno) super(Entry, self).on_duplicate(ar, master) dd.inject_field( 'pcsw.ClientContactType', 'is_bailiff', models.BooleanField( _("Debt collection agency"), default=False)) # dd.inject_field( # 'system.SiteConfig', # 'debts_bailiff_type', # models.ForeignKey("pcsw.ClientContactType", # blank=True, null=True, # verbose_name=_("Bailiff"), # related_name='bailiff_type_sites', # help_text=_("Client contact type for Bailiff."))) dd.inject_field( 'system.SiteConfig', 'master_budget',
verbose_name_plural = _("Teams") abstract = dd.is_abstract_model(__name__, 'Team') class Teams(dd.Table): model = 'teams.Team' required_roles = dd.login_required(dd.SiteStaff) column_names = 'ref name *' order_by = ["ref", "name"] insert_layout = """ ref name """ detail_layout = """ id ref name teams.UsersByTeam """ dd.inject_field( 'users.User', 'team', dd.ForeignKey('teams.Team', blank=True, null=True)) from lino.modlib.users.desktop import Users class UsersByTeam(Users): master_key = 'team'
column_names = "selected partner preview amount invoice_button *" class InvoicingsByGenerator(dd.Table): required_roles = dd.login_required(LedgerUser) model = dd.plugins.invoicing.item_model label = _("Invoicings") master_key = 'invoiceable' editable = False column_names = "voucher qty title description:20x1 #discount " \ "unit_price total_incl #total_base #total_vat *" invoiceable_label = dd.plugins.invoicing.invoiceable_label dd.inject_field('products.Product', 'tariff', dd.ForeignKey('invoicing.Tariff', blank=True, null=True)) dd.inject_field(dd.plugins.invoicing.voucher_model, 'invoicing_min_date', dd.DateField(_("Invoiceables from"), blank=True, null=True)) dd.inject_field(dd.plugins.invoicing.voucher_model, 'invoicing_max_date', dd.DateField(_("until"), blank=True, null=True)) dd.inject_field( dd.plugins.invoicing.item_model, 'invoiceable_type', dd.ForeignKey(ContentType, blank=True, null=True, verbose_name=format_lazy(u"{} {}", invoiceable_label, _('(type)')))) dd.inject_field( dd.plugins.invoicing.item_model, 'invoiceable_id',
return tostring(e) sender.plugins.memo.parser.register_django_model( 'order', Order, cmd=cmd, # title=lambda obj: obj.name ) # class OrderItem(ProductDocItem, SequencedVoucherItem): class OrderItem(SequencedVoucherItem): class Meta: app_label = 'orders' abstract = dd.is_abstract_model(__name__, 'OrderItem') verbose_name = _("Order item") verbose_name_plural = _("Order items") allow_cascaded_delete = 'voucher' voucher = dd.ForeignKey('orders.Order', related_name='items') # title = models.CharField(_("Heading"), max_length=200, blank=True) product = dd.ForeignKey('products.Product', blank=True, null=True) qty = dd.QuantityField(_("Quantity"), blank=True, null=True) # unit_price = dd.PriceField(_("Unit price"), blank=True, null=True) remark = models.CharField(_("Remark"), max_length=200, blank=True) dd.inject_field('ledger.Journal', 'room', dd.ForeignKey('cal.Room', blank=True, null=True)) from .ui import *
VatDocument. The following trick worked... but best is to store it in VatRegime, not per voucher. """ def set_default_item_vat(sender, instance=None, **kwargs): instance.item_vat = settings.SITE.get_item_vat(instance) # print("20130902 set_default_item_vat", instance) @dd.receiver(dd.post_analyze) def on_post_analyze(sender, **kw): for m in rt.models_by_base(VatDocument): dd.post_init.connect(set_default_item_vat, sender=m) # print('20130902 on_post_analyze installed receiver for',m) dd.inject_field('contacts.Partner', 'vat_regime', VatRegimes.field(blank=True)) def get_vat_regime_choices(country=None): vat_area = VatAreas.get_for_country(country) # print("20190405", vat_area) for r in VatRegimes.get_list_items(): if vat_area is None or r.vat_area is None or r.vat_area == vat_area: yield r @dd.chooser() def partner_vat_regime_choices(cls, country): return get_vat_regime_choices(country)
# GuestStates.excused.add_transition( # required_states='invited accepted absent gone') # GuestStates.absent.add_transition( # required_states='invited accepted excused gone') # GuestStates.present.add_transition( # required_states='invited accepted gone') #~ add = GuestStates.add_item #~ add('21', _("Waiting"),'waiting') # We inject three fields to cal.Guest. # Documentation and help_text are in book/docs/specs/cal.rst dd.inject_field( 'cal.Guest', 'waiting_since', models.DateTimeField(_("Waiting since"), editable=False, blank=True, null=True)) dd.inject_field( 'cal.Guest', 'busy_since', models.DateTimeField(_("Waiting until"), editable=False, blank=True, null=True)) dd.inject_field( 'cal.Guest', 'gone_since', models.DateTimeField(_("Present until"), editable=False, blank=True, null=True))
def set_upload_shortcuts(sender, **kw): """This is the successor for `quick_upload_buttons`.""" # remember that models might have been overridden. UploadType = sender.modules.uploads.UploadType for i in list(Shortcuts.items()): def f(obj, ar): if obj is None or ar is None: return E.div() try: utype = UploadType.objects.get(shortcut=i) except UploadType.DoesNotExist: return E.div() items = [] target = sender.modules.resolve(i.target) sar = ar.spawn_request( actor=target, master_instance=obj, known_values=dict(type=utype)) # param_values=dict(pupload_type=et)) n = sar.get_total_count() if n == 0: iar = target.insert_action.request_from( sar, master_instance=obj) btn = iar.ar2button( None, _("Upload"), icon_name="page_add", title=_("Upload a file from your PC to the server.")) items.append(btn) elif n == 1: after_show = ar.get_status() obj = sar.data_iterator[0] items.append(sar.renderer.href_button( dd.build_media_url(obj.file.name), _("show"), target='_blank', icon_name='page_go', style="vertical-align:-30%;", title=_("Open the uploaded file in a " "new browser window"))) after_show.update(record_id=obj.pk) items.append(sar.window_action_button( sar.ah.actor.detail_action, after_show, _("Edit"), icon_name='application_form', title=_("Edit metadata of the uploaded file."))) else: obj = sar.sliced_data_iterator[0] items.append(ar.obj2html( obj, pgettext("uploaded file", "Last"))) btn = sar.renderer.action_button( obj, sar, sar.bound_action, _("All {0} files").format(n), icon_name=None) items.append(btn) return E.div(*join_elems(items, ', ')) vf = dd.VirtualField(dd.DisplayField(i.text), f) dd.inject_field(i.model_spec, i.name, vf)
required_roles = dd.login_required(IntegrationStaff) model = 'active_job_search.Proof' detail_layout = """ date client company id spontaneous response remarks """ class ProofsByClient(Proofs): required_roles = dd.login_required(IntegrationAgent) master_key = 'client' column_names = "date company spontaneous response *" auto_fit_column_widths = True dd.inject_field( 'pcsw.Client', 'geographic_area', models.CharField( _("Geographic area"), blank=True, max_length=200, help_text=_( "The area for which we are seeking a job."))) dd.inject_field( 'pcsw.Client', 'child_custody', models.TextField( _("Child custody"), blank=True, help_text=_("Notes concerning child custody.")))
class RolesByPerson(Roles): """Shows all roles of a person.""" required_roles = dd.required(SimpleContactsUser) #~ required_user_level = None label = _("Contact for") master_key = 'person' column_names = 'company type *' auto_fit_column_widths = True hidden_columns = 'id' dd.inject_field( 'system.SiteConfig', 'next_partner_id', models.IntegerField( default=PARTNER_NUMBERS_START_AT, blank=True, null=True, verbose_name=_("Next partner id"), help_text=_("The next automatic id for any new partner."))) def site_setup(site): site.modules.countries.Places.set_detail_layout(""" name country type parent zip_code id PlacesByPlace contacts.PartnersByCity """)
def set_excerpts_actions(sender, **kw): """Installs (1) print management actions on models for which there is an excerpt type and (2) the excerpt shortcut fields defined in :class:`lino_xl.lib.excerpts.choicelists.Shortcuts`. """ # logger.info("20140401 %s.set_attest_actions()", __name__) # in case ExcerptType is overridden ExcerptType = sender.modules.excerpts.ExcerptType Excerpt = sender.modules.excerpts.Excerpt try: etypes = [(obj, obj.content_type) for obj in ExcerptType.objects.all()] except (OperationalError, ProgrammingError, UnresolvedChoice) as e: dd.logger.debug("Failed to set excerpts actions : %s", e) # Happens e.g. when the database has not yet been migrated etypes = [] for atype, ct in etypes: if ct is not None: m = ct.model_class() if m is not None: # e.g. database contains types for # models that existed before but have # been removed an = atype.get_action_name() m.define_action( **{an: CreateExcerpt(atype, six.text_type(atype))}) # dd.logger.info("Added print action to %s", m) # if atype.certifying and not issubclass(m, Certifiable): # m.define_action( # clear_printed=ClearCache()) # An attestable model must also inherit # :class:`lino.mixins.printable.BasePrintable` or some subclass # thereof. for i in Shortcuts.items(): def f(obj, ar): if ar is None: return '' if obj is None: return E.div() try: et = ExcerptType.objects.get(shortcut=i) except ExcerptType.DoesNotExist: return E.div() items = [] if True: sar = ar.spawn(ExcerptsByOwner, master_instance=obj, param_values=dict(excerpt_type=et)) n = sar.get_total_count() if n > 0: ex = sar.sliced_data_iterator[0] items.append(ar.obj2html(ex, _("Last"))) ba = sar.bound_action btn = sar.renderer.action_button(obj, sar, ba, "%s (%d)" % (_("All"), n), icon_name=None) items.append(btn) ia = getattr(obj, et.get_action_name()) btn = ar.instance_action_button(ia, _("Create"), icon_name=None) items.append(btn) else: ot = ContentType.objects.get_for_model(obj.__class__) qs = Excerpt.objects.filter(owner_id=obj.pk, owner_type=ot, excerpt_type=et) if qs.count() > 0: ex = qs[0] txt = ExcerptsByOwner.format_excerpt(ex) items.append(ar.obj2html(ex, txt)) return E.div(*join_elems(items, ', ')) vf = dd.VirtualField(dd.DisplayField(i.text), f) dd.inject_field(i.model_spec, i.name, vf)