class Concept(mixins.BabelNamed): """A word and its translation in different languages. """ class Meta: verbose_name = _("Concept") verbose_name_plural = _("Concepts") abbr = dd.BabelCharField(_("Abbreviation"), max_length=30, blank=True) wikipedia = dd.BabelCharField(_("Wikipedia"), max_length=200, blank=True) definition = dd.BabelTextField(_("Definition"), blank=True) is_jargon_domain = models.BooleanField( _("Jargon domain"), help_text= _("Whether this concept designates a domain of specialized vocabulary." ), default=False) def summary_row(self, ar=None): if self.abbr: return [ "%s (%s)" % (dd.babelattr(self, 'name'), dd.babelattr(self, 'abbr')) ] return [dd.babelattr(self, 'name')]
class ExcerptTitle(BabelNamed): """Mixin for models like :class:`lino_welfare.modlib.aids.models.AidType` and :class:`lino.modlib.courses.models.Line`. .. attribute:: name The designation of this row as seen by the user e.g. when selecting an instance of this model. One field for every :attr:`language <lino.core.site.Site.language>`. .. attribute:: excerpt_title The text to print as title in confirmations. One field for every :attr:`language <lino.core.site.Site.language>`. If this is empty, then :attr:`name` is used. """ class Meta: abstract = True excerpt_title = dd.BabelCharField( _("Excerpt title"), max_length=200, blank=True, help_text=_("The title to be used when printing an excerpt.")) def get_excerpt_title(self): return dd.babelattr(self, 'excerpt_title') or unicode(self)
class CompanyType(mixins.BabelNamed): class Meta(object): app_label = 'contacts' # avoid RemovedInDjango19Warning abstract = dd.is_abstract_model(__name__, 'CompanyType') verbose_name = _("Organization type") verbose_name_plural = _("Organization types") abbr = dd.BabelCharField(_("Abbreviation"), max_length=30, blank=True)
class EventType(mixins.BabelNamed, mixins.Sequenced, MailableType): """The possible value of the :attr:`Event.type` field. Example content: .. lino2rst:: rt.show(cal.EventTypes, limit=5) .. attribute:: is_appointment Whether events of this type should be considered "appointments" (i.e. whose time and place have been agreed upon with other users or external parties). The table (:class:`EventsByDay` and :class:`MyEvents`) show only events whose type has the `is_appointment` field checked. """ templates_group = 'cal/Event' class Meta: app_label = 'cal' abstract = dd.is_abstract_model(__name__, 'EventType') verbose_name = _("Calendar Event Type") verbose_name_plural = _("Calendar Event Types") ordering = ['seqno'] description = dd.RichTextField(_("Description"), blank=True, format='html') is_appointment = models.BooleanField(_("Event is an appointment"), default=True) all_rooms = models.BooleanField(_("Locks all rooms"), default=False) locks_user = models.BooleanField( _("Locks the user"), help_text=_("Whether events of this type make the user unavailable " "for other locking events at the same time."), default=False) start_date = models.DateField(verbose_name=_("Start date"), blank=True, null=True) event_label = dd.BabelCharField(_("Event label"), max_length=200, blank=True) # , default=_("Calendar entry")) # default values for a Babelfield don't work as expected max_conflicting = models.PositiveIntegerField( _("Simultaneous events"), help_text=_("How many conflicting events should be tolerated."), default=1) def __unicode__(self): # when selecting an Event.event_type it is more natural to # have the event_label. It seems that the current `name` field # is actually never used. return settings.SITE.babelattr(self, 'event_label') \ or settings.SITE.babelattr(self, 'name')
class ExcerptTitle(BabelNamed): class Meta: abstract = True excerpt_title = dd.BabelCharField( _("Excerpt title"), max_length=200, blank=True, help_text=_( "The title to be used when printing an excerpt.")) def get_excerpt_title(self): return dd.babelattr(self, 'excerpt_title') or str(self)
class EventType(mixins.BabelNamed, Referrable, mixins.Sequenced, MailableType): templates_group = 'cal/Event' ref_max_length = 4 class Meta: app_label = 'cal' abstract = dd.is_abstract_model(__name__, 'EventType') verbose_name = _("Calendar entry type") verbose_name_plural = _("Calendar entry types") ordering = ['seqno'] description = dd.RichTextField(_("Description"), blank=True, format='html') is_appointment = models.BooleanField(_("Appointment"), default=True) all_rooms = models.BooleanField(_("Locks all rooms"), default=False) locks_user = models.BooleanField(_("Locks the user"), default=False) force_guest_states = models.BooleanField(_("Automatic presences"), default=False) fill_presences = models.BooleanField(_("Fill guests"), default=False) start_date = models.DateField(verbose_name=_("Start date"), blank=True, null=True) event_label = dd.BabelCharField(_("Entry label"), max_length=200, blank=True) # , default=_("Calendar entry")) # default values for a Babelfield don't work as expected max_conflicting = models.PositiveIntegerField(_("Simultaneous entries"), default=1) max_days = models.PositiveIntegerField(_("Maximum days"), default=1) transparent = models.BooleanField(_("Transparent"), default=False) planner_column = PlannerColumns.field(blank=True) # default_duration = models.TimeField( # _("Default duration"), blank=True, null=True) default_duration = dd.DurationField(_("Default duration"), blank=True, null=True)
class PropChoice(dd.Model): class Meta: verbose_name = _("Property Choice") verbose_name_plural = _("Property Choices") unique_together = ['type', 'value'] type = dd.ForeignKey(PropType, verbose_name=_("Property Type")) value = models.CharField(max_length=settings.SITE.propvalue_max_length, verbose_name=_("Value")) text = dd.BabelCharField(max_length=200, verbose_name=_("Designation"), blank=True) def save(self, *args, **kw): if not self.text: self.text = self.value r = super(PropChoice, self).save(*args, **kw) return r def __str__(self): return dd.babelattr(self, 'text')
class PropChoice(dd.Model): """A Choice for a given PropType. `text` is the text to be displayed in combo boxes. `value` is the value to be stored in :attr:`PropValue.value`, it must be unique for all PropChoices of a given PropType. Choices for a given PropType will be sorted on `value` (we might make this more customizable if necessary by adding a new field `sort_text` and/or an option to sort on text instead of value) When configuring your property choices, be aware of the fact that existing property occurences will *not* change when you change the `value` of a property choice. """ class Meta: verbose_name = _("Property Choice") verbose_name_plural = _("Property Choices") unique_together = ['type', 'value'] type = models.ForeignKey(PropType, verbose_name=_("Property Type")) value = models.CharField(max_length=settings.SITE.propvalue_max_length, verbose_name=_("Value")) text = dd.BabelCharField(max_length=200, verbose_name=_("Designation"), blank=True) def save(self, *args, **kw): if not self.text: self.text = self.value r = super(PropChoice, self).save(*args, **kw) return r def __unicode__(self): return dd.babelattr(self, 'text')
class Page(Referrable, Hierarchical, Sequenced, Publishable): class Meta: verbose_name = _("Node") verbose_name_plural = _("Nodes") title = dd.BabelCharField(_("Title"), max_length=200, blank=True) body = dd.BabelTextField(_("Body"), blank=True, format='plain') raw_html = models.BooleanField(_("raw html"), default=False) @classmethod def get_dashboard_items(cls, user): obj = cls.get_by_ref('index', None) if obj is not None: yield obj def get_absolute_url(self, **kwargs): if self.ref: if self.ref != 'index': return dd.plugins.pages.build_plain_url(self.ref, **kwargs) return dd.plugins.pages.build_plain_url(**kwargs) def get_sidebar_caption(self): if self.title: return dd.babelattr(self, 'title') if self.ref == 'index': return str(_('Home')) if self.ref: return self.ref return str(self.id) #~ if self.ref or self.parent: #~ return self.ref #~ return unicode(_('Home')) def get_sidebar_item(self, request, other): kw = dict() add_user_language(kw, request) url = self.get_absolute_url(**kw) a = E.a(self.get_sidebar_caption(), href=url) if self == other: return E.li(a, **{'class': 'active'}) return E.li(a) def get_sidebar_html(self, request): items = [] #~ loop over top-level nodes for n in Page.objects.filter(parent__isnull=True).order_by('seqno'): #~ items += [li for li in n.get_sidebar_items(request,self)] items.append(n.get_sidebar_item(request, self)) if self.is_parented(n): children = [] for ch in n.children.order_by('seqno'): children.append(ch.get_sidebar_item(request, self)) if len(children): items.append(E.ul(*children, **{'class': 'nav nav-list'})) e = E.ul(*items, **{'class': 'nav nav-list'}) return tostring_pretty(e) def get_sidebar_menu(self, request): qs = Page.objects.filter(parent__isnull=True) #~ qs = self.children.all() yield ('/', 'index', str(_('Home'))) #~ yield ('/downloads/', 'downloads', 'Downloads') #~ yield ('/about', 'about', 'About') #~ if qs is not None: for obj in qs: if obj.ref and obj.title: yield ('/' + obj.ref, obj.ref, dd.babelattr(obj, 'title'))
class Page(mixins.Referrable, mixins.Hierarchical, mixins.Sequenced): """ Deserves more documentation. """ class Meta: verbose_name = _("Node") verbose_name_plural = _("Nodes") title = dd.BabelCharField(_("Title"), max_length=200, blank=True) body = dd.BabelTextField(_("Body"), blank=True, format='plain') raw_html = models.BooleanField(_("raw html"), default=False) def get_absolute_url(self): if self.ref: if self.ref != 'index': return PAGES.build_plain_url(self.ref) return PAGES.build_plain_url() def get_sidebar_caption(self): if self.title: return dd.babelattr(self, 'title') if self.ref == 'index': return unicode(_('Home')) if self.ref: return self.ref return str(self.id) #~ if self.ref or self.parent: #~ return self.ref #~ return unicode(_('Home')) def get_sidebar_item(self, request, other): a = E.a(self.get_sidebar_caption(), href=self.get_absolute_url()) if self == other: return E.li(a, class_='active') return E.li(a) def get_sidebar_html(self, request): items = [] #~ loop over top-level nodes for n in Page.objects.filter(parent__isnull=True).order_by('seqno'): #~ items += [li for li in n.get_sidebar_items(request,self)] items.append(n.get_sidebar_item(request, self)) if self.is_parented(n): children = [] for ch in n.children.order_by('seqno'): children.append(ch.get_sidebar_item(request, self)) if len(children): items.append(E.ul(*children, class_='nav nav-list')) e = E.ul(*items, class_='nav nav-list') return E.tostring_pretty(e) def get_sidebar_menu(self, request): #~ qs = self.get_siblings() qs = Page.objects.filter(parent__isnull=True) #~ qs = self.children.all() yield ('/', 'index', unicode(_('Home'))) #~ yield ('/downloads/', 'downloads', 'Downloads') #~ yield ('/about', 'about', 'About') #~ if qs is not None: for obj in qs: if obj.ref and obj.title: yield ('/' + obj.ref, obj.ref, dd.babelattr(obj, 'title'))
class Journal(mixins.BabelNamed, mixins.Sequenced, mixins.Referrable, PrintableType): """The model used to store **journals**. See :ref:`cosi.specs.ledger.journals`. **Fields:** .. attribute:: ref .. attribute:: trade_type Pointer to :class:`TradeTypes`. .. attribute:: voucher_type Pointer to an item of :class:`VoucherTypes`. .. attribute:: journal_group Pointer to an item of :class:`JournalGroups`. .. attribute:: yearly_numbering Whether the :attr:`number<lino_cosi.lib.ledger.models.Voucher.number>` of vouchers should restart at 1 every year. .. attribute:: force_sequence .. attribute:: account .. attribute:: printed_name .. attribute:: dc The primary booking direction. In a journal of *sales invoices* this should be *Debit* (checked), because a positive invoice total should be *debited* from the customer's account. In a journal of *purchase invoices* this should be *Credit* (not checked), because a positive invoice total should be *credited* from the supplier's account. In a journal of *bank statements* this should be *Debit* (checked), because a positive balance change should be *debited* from the bank's general account. In a journal of *payment orders* this should be *Credit* (not checked), because a positive total means an "expense" and should be *credited* from the journal's general account. In all financial vouchers, the amount of every item increases the total if its direction is opposite of the primary direction. .. attribute:: auto_check_clearings Whether to automatically check and update the 'cleared' status of involved transactions when (de)registering a voucher of this journal. This can be temporarily disabled e.g. by batch actions in order to save time. .. attribute:: template See :attr:`PrintableType.template <lino.mixins.printable.PrintableType.template>`. """ class Meta: app_label = 'ledger' verbose_name = _("Journal") verbose_name_plural = _("Journals") trade_type = TradeTypes.field(blank=True) voucher_type = VoucherTypes.field() journal_group = JournalGroups.field() auto_check_clearings = models.BooleanField( _("Check clearing"), default=True, help_text=_("Automatically update the cleared status of involved " "transactions when (de)registering a voucher of this " "journal")) force_sequence = models.BooleanField(_("Force chronological sequence"), default=False) account = dd.ForeignKey('accounts.Account', blank=True, null=True) printed_name = dd.BabelCharField(_("Printed document designation"), max_length=100, blank=True) dc = DebitOrCreditField(_("Primary booking direction")) yearly_numbering = models.BooleanField(_("Yearly numbering"), default=False) # invert_due_dc = models.BooleanField( # _("Invert booking direction"), # help_text=_("Whether to invert booking direction of due movement."), # default=True) # @dd.chooser() # def account_choices(cls, chart): # fkw = dict(type=AccountTypes.bank_accounts) # return rt.modules.accounts.Account.objects.filter(chart=chart, **fkw) def get_doc_model(self): """The model of vouchers in this Journal. """ # print self,DOCTYPE_CLASSES, self.doctype return self.voucher_type.model #~ return DOCTYPES[self.doctype][0] def get_doc_report(self): return self.voucher_type.table_class #~ return DOCTYPES[self.doctype][1] def get_voucher(self, year=None, number=None, **kw): cl = self.get_doc_model() kw.update(journal=self, accounting_period__year=year, number=number) return cl.objects.get(**kw) def create_voucher(self, **kw): """Create an instance of this Journal's voucher model (:meth:`get_doc_model`). """ cl = self.get_doc_model() kw.update(journal=self) try: doc = cl() # ~ doc = cl(**kw) # wouldn't work. See Django ticket #10808 #~ doc.journal = self for k, v in kw.items(): setattr(doc, k, v) #~ print 20120825, kw except TypeError: #~ print 20100804, cl raise doc.on_create(None) #~ doc.full_clean() #~ doc.save() return doc def get_allowed_accounts(self, **kw): if self.trade_type: kw[self.trade_type.name + '_allowed'] = True # kw.update(chart=self.chart) return rt.modules.accounts.Account.objects.filter(**kw) def get_next_number(self, voucher): # ~ self.save() # 20131005 why was this? cl = self.get_doc_model() flt = dict() if self.yearly_numbering: flt.update(accounting_period__year=voucher.accounting_period.year) d = cl.objects.filter(journal=self, **flt).aggregate(models.Max('number')) number = d['number__max'] #~ logger.info("20121206 get_next_number %r",number) if number is None: return 1 return number + 1 def __str__(self): # s = super(Journal, self).__str__() s = dd.babelattr(self, 'name') if self.ref: s += " (%s)" % self.ref #~ return '%s (%s)' % (d.BabelNamed.__unicode__(self),self.ref or self.id) return s #~ return self.ref +'%s (%s)' % mixins.BabelNamed.__unicode__(self) #~ return self.id +' (%s)' % mixins.BabelNamed.__unicode__(self) def save(self, *args, **kw): #~ self.before_save() r = super(Journal, self).save(*args, **kw) self.after_save() return r def after_save(self): pass def full_clean(self, *args, **kw): if self.dc is None: if self.trade_type: self.dc = self.trade_type.dc elif self.account: self.dc = self.account.type.dc else: self.dc = DEBIT # cannot be NULL if not self.name: self.name = self.id #~ if not self.pos: #~ self.pos = self.__class__.objects.all().count() + 1 super(Journal, self).full_clean(*args, **kw) def disable_voucher_delete(self, doc): # print "pre_delete_voucher", doc.number, self.get_next_number() if self.force_sequence: if doc.number + 1 != self.get_next_number(doc): return _("%s is not the last voucher in journal" % unicode(doc)) def get_template_groups(self): """Here we override the class method by an instance method. This means that we must also override all other methods of Printable who call the *class* method. This is currently only :meth:`template_choices`. """ return [self.voucher_type.model.get_template_group()] @dd.chooser(simple_values=True) def template_choices(cls, build_method, voucher_type): # Overrides PrintableType.template_choices to not use the class # method `get_template_groups`. if not voucher_type: return [] #~ print 20131006, voucher_type template_groups = [voucher_type.model.get_template_group()] return cls.get_template_choices(build_method, template_groups)