Example #1
0
 def get_change_subject(self, ar, cw):
     ctx = dict(user=ar.user, what=str(self))
     if cw is None:
         return _("{user} submitted ticket {what}").format(**ctx)
     if len(list(cw.get_updates())) == 0:
         return
     return _("{user} modified {what}").format(**ctx)
Example #2
0
def objects():

    yield lib_objects()

    GuestRole = rt.models.cal.GuestRole

    yield GuestRole(**dd.str2kw('name', _("Participant")))
    yield GuestRole(**dd.str2kw('name', _("Guide")))
    yield GuestRole(**dd.str2kw('name', _("Teacher")))

    EventType = rt.models.cal.EventType
    RecurrentEvent = rt.models.cal.RecurrentEvent
    Recurrencies = rt.models.cal.Recurrencies
    DEMO_START_YEAR = rt.models.cal.DEMO_START_YEAR

    holidays = EventType.objects.get(
        **dd.str2kw('name', _("Holidays")))
    yield RecurrentEvent(
        event_type=holidays,
        every_unit=Recurrencies.yearly,
        monday=True, tuesday=True, wednesday=True, thursday=True,
        friday=True, saturday=True, sunday=True,
        every=1,
        start_date=datetime.date(
            year=DEMO_START_YEAR,
            month=7, day=1),
        end_date=datetime.date(
            year=DEMO_START_YEAR,
            month=8, day=31),
        **dd.str2kw('name', _("Summer holidays")))
Example #3
0
def objects():
    UserTypes = rt.models.users.UserTypes
    Company = rt.models.contacts.Company
    Product = rt.models.products.Product
    Account = rt.models.accounts.Account
    AccountTypes = rt.models.accounts.AccountTypes
    CommonAccounts = rt.models.accounts.CommonAccounts

    yield create_user("daniel", UserTypes.therapist)
    yield create_user("elmar", UserTypes.therapist)
    yield create_user("lydia", UserTypes.secretary)

    # yield skills_objects()

    obj = Company(
        name="Tough Thorough Thought Therapies",
        country_id="BE", vat_id="BE12 3456 7890")
    yield obj
    settings.SITE.site_config.update(site_company=obj)

    yield named(Product, _("Group therapy"), sales_price=30)
    indacc = named(
        Account, _("Sales on individual therapies"),
        group=CommonAccounts.sales.get_object().group,
        type=AccountTypes.incomes, ref="7010")
    yield indacc
    yield named(
        Product, _("Individual therapy"),
        sales_price=60, sales_account=indacc)
    yield named(Product, _("Other"), sales_price=35)
Example #4
0
def update_widgets_for(ar, user):
    
    available = set([i.name for i in get_available_items(user)])
    
    Widget = rt.models.dashboard.Widget
    qs = Widget.objects.filter(user=user).order_by('seqno')
    
    def ok(ar):
        seqno = 0
        for w in qs:
            if w.item_name in available:
                available.remove(w.item_name)
                seqno = max(seqno, w.seqno)
            else:
                w.delete()
        up = user.get_preferences()
        for name in available:
            seqno += 1
            obj = Widget(user=user, item_name=name, seqno=seqno)
            obj.full_clean()
            obj.save()
            # print(20161128, "created item", obj)

        up.invalidate()
        ar.set_response(refresh=True)
        
    if qs.count() > 0:
        ar.confirm(
            ok, _("This will overwrite your current settings."),
            _("Are you sure?"))
    else:
        ok(ar)
Example #5
0
    def run_from_ui(self, ar, **kw):

        if len(ar.selected_rows) == 1:
            obj = ar.selected_rows[0]
            bm = obj.get_build_method()
            mf = bm.get_target(self, obj)
            leaf = mf.parts[-1]
            if obj.build_time is None:
                obj.build_target(ar)
                ar.info("%s has been built.", leaf)
            else:
                ar.info("Reused %s from cache.", leaf)

            url = mf.get_url(ar.request)
            self.notify_done(ar, bm, leaf, url, **kw)
            ar.set_response(refresh=True)
            return

        def ok(ar2):
            # qs = [ar.actor.get_row_by_pk(pk) for pk in ar.selected_pks]
            mf = self.print_multiple(ar, ar.selected_rows)
            ar2.success(open_url=mf.get_url(ar.request))
            # kw.update(refresh_all=True)
            # return kw
        msg = _("This will print %d rows.") % len(ar.selected_rows)
        ar.confirm(ok, msg, _("Are you sure?"))
Example #6
0
    def run_from_ui(self, ar):
        elem = ar.selected_rows[0]

        def doit(ar):
            elem.clear_cache()
            ar.success(_("%s printable cache has been cleared.") %
                       elem, refresh=True)

        t = elem.get_cache_mtime()
        if t is not None:
            # set microseconds to those of the stored field because
            # Django DateTimeField can have microseconds precision or
            # not depending on the database backend.

            t = datetime.datetime(
                t.year, t.month, t.day, t.hour,
                t.minute, t.second, elem.build_time.microsecond)
            if settings.USE_TZ:
                t = make_aware(t)
            if t != elem.build_time:
                # logger.info("20140313 %r != %r", t, elem.build_time)
                return ar.confirm(
                    doit,
                    _("This will discard all changes in the generated file."),
                    _("Are you sure?"))
        return doit(ar)
Example #7
0
def skills_objects():
    "was previously in skills.fixtures.demo2"

    Skill = rt.models.skills.Skill
    Competence = rt.models.skills.Competence
    Demand = rt.models.skills.Demand
    # Ticket = rt.models.tickets.Ticket
    User = rt.models.users.User

    yield named(Skill, _('Psychotherapy'))
    yield named(Skill, _('Psychiatry'))

    SKILLS = Cycler(Skill.objects.all())
    END_USERS = Cycler(dd.plugins.skills.end_user_model.objects.all())

    i = 0
    for j in range(2):
        for u in User.objects.all():
            i += 1
            yield Competence(user=u, faculty=SKILLS.pop())
            if i % 2:
                yield Competence(user=u, faculty=SKILLS.pop())
            if i % 3:
                yield Competence(
                    user=u, faculty=SKILLS.pop(),
                    end_user=END_USERS.pop())
            
    for i, t in enumerate(
            dd.plugins.skills.demander_model.objects.all()):
        yield Demand(demander=t, skill=SKILLS.pop())
        if i % 3:
            yield Demand(demander=t, skill=SKILLS.pop())
Example #8
0
    def get_checkdata_problems(self, obj, fix=False):
        if obj.event_type_id and obj.event_type.force_guest_states:
            if obj.state.guest_state:
                qs = obj.guest_set.exclude(state=obj.state.guest_state)
                if qs.exists():
                    msg = _("Some guests have another state than {0}")
                    yield (True, msg.format(obj.state.guest_state))
                    if fix:
                        for g in qs:
                            g.state = obj.state.guest_state
                            g.full_clean()
                            g.save()

        if not obj.state.edit_guests:
            return
        # existing = set([g.partner.pk for g in obj.guest_set.all()])
        # if len(existing) == 0:
        guests = obj.guest_set.all()
        if not guests.exists():
            suggested = list(obj.suggest_guests())
            if len(suggested) > 0:
                msg = _("No participants although {0} suggestions exist.")
                yield (True, msg.format(len(suggested)))
                if fix:
                    for g in suggested:
                        g.save()
Example #9
0
    def description(cls, fld, ar):
        if ar is None:
            return ''
        elems = [fld.help_text, E.br()]
        def x(label, lst, xlst):
            if lst is None:
                return
            spec = ' '.join([i.name or i.value for i in lst])
            if xlst is not None:
                spec += ' ' + ' '.join([
                    "!"+(i.name or i.value) for i in xlst])
            spec = spec.strip()
            if spec:
                elems.extend([label, " ", spec, E.br()])

        x(_("columns"), fld.vat_columns, fld.exclude_vat_columns)
        x(_("regimes"), fld.vat_regimes, fld.exclude_vat_regimes)
        x(_("classes"), fld.vat_classes, fld.exclude_vat_classes)


        elems += [
            fld.__class__.__name__, ' ',
            DCLABELS[fld.dc], 
            "" if fld.both_dc else " only",
            E.br()]

        if fld.observed_fields:
            elems += [
                _("Sum of"), ' ',
                ' '.join([i.name for i in fld.observed_fields]),
                E.br()]

        return E.div(*forcetext(elems))
Example #10
0
    def get_notify_message(self, ar, cw):
        """Returns the text of the notification message to emit.

        The default implementation returns a message of style
        "{object} has been modified by {user}" followed by a summary
        of the changes.  

        Application code can override this. Returning None or an empty
        string means to suppress notification.

        """
        if cw is None:
            return
        items = list(cw.get_updates_html())
        if len(items) == 0:
            return
        elems = [E.p(
            ar.obj2html(self),
            ' ', _("has been modified by {user}").format(
                user=ar.get_user()),
            ":")]
        elems.append(E.ul(*items))
        if False:
            elems.append(E.p(_(
                "Subsequent changes to {obj} will not be notified "
                "until you visit {url} and mark this notification "
                "as seen.").format(
                url=settings.SITE.server_url or "Lino",
                obj=self.get_notify_owner(ar))))
        return E.tostring(E.div(*elems))
Example #11
0
    def run_from_ui(self, ar, **kw):

        lcd = settings.SITE.confdirs.LOCAL_CONFIG_DIR
        if lcd is None:
            # ar.info("No local config directory in %s " %
            #         settings.SITE.confdirs)
            raise Warning("No local config directory. "
                          "Contact your system administrator.")

        elem = ar.selected_rows[0]
        bm = elem.get_build_method()
        leaf = bm.get_template_leaf(self, elem)

        filename = bm.get_template_file(ar, self, elem)
        local_file = None
        groups = elem.get_template_groups()
        assert len(groups) > 0
        for grp in reversed(groups):
            # subtle: if there are more than 1 groups
            parts = [grp, leaf]
            local_file = os.path.join(lcd.name, *parts)
            if filename == local_file:
                break

        parts = ['webdav', 'config'] + parts
        url = settings.SITE.build_media_url(*parts)
        # url = ar.build_webdav_uri(url)

        if not (settings.SITE.webdav_protocol or has_davlink):
            msg = "cp %s %s" % (filename, local_file)
            ar.info(msg)
            raise Warning("WebDAV is not enabled. "
                          "Contact your system administrator.")
            
        def doit(ar):
            ar.info("Going to open url: %s " % url)
            if settings.SITE.webdav_protocol:
                ar.success(open_url=url)
            else:
                ar.success(open_webdav_url=url)
            # logger.info('20140313 EditTemplate %r', kw)
    
        if filename == local_file:
            doit(ar)
        else:
            ar.info("Gonna copy %s to %s", filename, local_file)
            
            def ok(ar2):
                logger.info(
                    "%s made local template copy %s", ar.user, local_file)
                rt.makedirs_if_missing(os.path.dirname(local_file))
                shutil.copy(filename, local_file)
                doit(ar2)

            msg = _(
                "Before you can edit this template we must create a "
                "local copy on the server. "
                "This will exclude the template from future updates.")
            ar.confirm(ok, msg, _("Are you sure?"))
Example #12
0
 def get_title_tags(self, ar):
     for t in super(Pupil, self).get_title_tags(ar):
         yield t
     pv = ar.param_values
     if pv.show_members:
         yield "{0}:{1}".format(_("Members"), pv.show_members)
     if pv.show_ckk:
         yield "{0}:{1}".format(_("CKK"), pv.show_ckk)
Example #13
0
def objects():

    Account = rt.modules.accounts.Account
    JournalGroups = rt.modules.ledger.JournalGroups
    BankStatement = rt.modules.finan.BankStatement
    PaymentOrder = rt.modules.finan.PaymentOrder
    DisbursementOrdersByJournal = rt.modules.finan.DisbursementOrdersByJournal
    InvoicesByJournal = rt.modules.vatless.InvoicesByJournal
    ProjectInvoicesByJournal = rt.modules.vatless.ProjectInvoicesByJournal
    MatchRule = rt.modules.ledger.MatchRule
    a4400 = Account.objects.get(ref="4400")
    a4450 = Account.objects.get(ref="4450")
    a5800 = Account.objects.get(ref="5800")

    kw = dict(journal_group=JournalGroups.reg)
    kw.update(trade_type='purchases', ref="REG")
    kw.update(dd.str2kw('name', _("Incoming invoices")))
    kw.update(dc=CREDIT)
    yield ProjectInvoicesByJournal.create_journal(**kw)

    kw.update(ref="SREG")
    kw.update(dd.str2kw('name', _("Collective purchase invoices")))
    yield InvoicesByJournal.create_journal(**kw)

    kw.update(dd.str2kw('name', _("Disbursement orders")))
    kw.update(account='4450', ref="AAW")
    kw.update(journal_group=JournalGroups.anw)
    # kw.update(dc=CREDIT)
    # kw.update(invert_due_dc=False)
    jnl = DisbursementOrdersByJournal.create_journal(**kw)
    yield jnl
    yield MatchRule(journal=jnl, account=a4400)

    if dd.is_installed('client_vouchers'):
        ClientVoucher = rt.modules.client_vouchers.ClientVoucher
        kw = dict(journal_group=JournalGroups.aids)
        kw.update(trade_type='aids', ref="AIDS")
        kw.update(dd.str2kw('name', _("Aid allocations")))
        jnl = ClientVoucher.create_journal(**kw)
        yield jnl
        yield MatchRule(journal=jnl, account=a4400)

    kw = dict()
    # kw.update(journal_group=JournalGroups.tre)
    # kw.update(dd.str2kw('name', _("KBC")))
    # kw.update(account='5500', ref="KBC")
    # jnl = BankStatement.create_journal(**kw)
    # yield jnl
    # yield MatchRule(journal=jnl, account=a4450)
    # yield MatchRule(journal=jnl, account=a5800)

    kw.update(journal_group=JournalGroups.zau)
    kw.update(dd.str2kw('name', _("KBC Payment Orders")))
    kw.update(account='5800', ref="ZKBC")
    kw.update(dc=CREDIT)
    jnl = PaymentOrder.create_journal(**kw)
    yield jnl
    yield MatchRule(journal=jnl, account=a4450)
Example #14
0
 def get_change_subject(self, ar, cw):
     ctx = dict(user=ar.user, what=str(self))
     if cw is None:
         return _("{user} created {what}").format(**ctx)
         # msg = _("has been created by {user}").format(**ctx)
         # return "{} {}".format(self, msg)
     if len(list(cw.get_updates())) == 0:
         return
     return _("{user} modified {what}").format(**ctx)
Example #15
0
    def get_detail_title(self, ar, obj):
        """Overrides the default beaviour

        """
        me = ar.get_user()
        pk = obj.votable.pk
        if me == obj.user:
            return _("My vote about #{}").format(pk)
        else:
            return _("{}'s vote about #{}").format(obj.user, pk)
Example #16
0
 def get_checkdata_problems(self, obj, fix=False):
     if not obj.has_conflicting_events():
         return
     qs = obj.get_conflicting_events()
     num = qs.count()
     if num == 1:
         msg = _("Event conflicts with {0}.").format(qs[0])
     else:
         msg = _("Event conflicts with {0} other events.").format(num)
     yield (False, msg)
Example #17
0
File: ui.py Project: khchine5/xl
 def get_title_tags(self, ar):
     pv = ar.param_values
     if pv.start_period:
         if pv.end_period:
             yield _("Periods {}...{}").format(
                 pv.start_period, pv.end_period)
         else:
             yield _("Period {}").format(pv.start_period)
     else:
         yield str(_("All periods"))
Example #18
0
 def get_checkdata_problems(self, obj, fix=False):
     if obj.account_id:
         if obj.account.needs_partner:
             if not obj.partner_id:
                 if obj.voucher.journal.refuse_missing_partner():
                     yield (False, _("Account {} needs a partner"))
         else:
             if obj.partner_id:
                 yield (False,
                        _("Account {} cannot be used with a partner"))
Example #19
0
def objects():

    PaperType = rt.models.sales.PaperType
    bm = rt.models.printing.BuildMethods.get_system_default()
    yield PaperType(
        template="DefaultLetter" + bm.template_ext,
        **dd.str2kw('name', _("Letter paper")))
    yield PaperType(
        template="DefaultBlank" + bm.template_ext,
        **dd.str2kw('name', _("Blank paper")))
Example #20
0
def override_field_names(sender=None, **kwargs):
    for m in rt.models_by_base(FinancialVoucher):
        dd.update_field(
            m, 'narration', verbose_name=_("Internal reference"))
        dd.update_field(
            m, 'item_remark', verbose_name=_("External reference"))
    for m in rt.models_by_base(FinancialVoucherItem):
        # dd.update_field(
        #     m, 'narration', verbose_name=_("Internal reference"))
        dd.update_field(
            m, 'remark', verbose_name=_("External reference"))
Example #21
0
    def get_parameter_fields(cls, **fields):
        fields.update(
            show_members=dd.YesNo.field(
                _("Members"), blank=True,
                help_text=_(
                    "Show those whose 'Member until' is after today.")),
            show_ckk=dd.YesNo.field(_("CKK"), blank=True),
            show_lfv=dd.YesNo.field(_("LFV"), blank=True),
            show_raviva=dd.YesNo.field(_("Raviva"), blank=True))

        return super(Pupil, cls).get_parameter_fields(**fields)
Example #22
0
 def full_clean(self, *args, **kw):
     if self.periods <= 0:
         raise ValidationError(_("Periods must be > 0"))
     if self.distribute and self.monthly_rate:
         raise ValidationError(
             # _("Cannot set both 'Distribute' and 'Monthly rate'"))
             _("Cannot set 'Distribute' when 'Monthly rate' is %r") % self.monthly_rate)
     # self.account_type = self.account.type
     # if not self.account_type:
         # ~ raise ValidationError(_("Budget entry #%d has no account_type") % obj2unicode(self))
     super(Entry, self).full_clean(*args, **kw)
Example #23
0
def objects():
    Group = rt.models.groups.Group
    User = rt.models.users.User
    UserTypes = rt.models.users.UserTypes

    yield named(Group, _("Hitchhiker's Guide to the Galaxy"))
    yield named(Group, _("Star Trek"))
    yield named(Group, _("Harry Potter"))

    yield User(username="******", user_type=UserTypes.user)
    yield User(username="******", user_type=UserTypes.user)
    yield User(username="******", user_type=UserTypes.user)
Example #24
0
 def get_actors(self):
     """Return a list of the actors of this budget."""
     attname = "_cached_actors"
     if hasattr(self, attname):
         return getattr(self, attname)
     l = list(self.actor_set.all())
     if len(l) > 0:
         main_header = _("Common")
     else:
         main_header = _("Amount")
     l.insert(0, MainActor(self, main_header))
     setattr(self, attname, l)
     return l
Example #25
0
 def get_rfc_description(self, ar):
     html = ''
     _ = gettext
     if self.description:
         # html += tostring(E.b(_("Description")))
         html += ar.parse_memo(self.description)
     if self.upgrade_notes:
         html += tostring(E.b(_("Resolution"))) + ": "
         html += ar.parse_memo(self.upgrade_notes)
     if self.duplicate_of_id:
         html += tostring(_("Duplicate of")) + " "
         html += tostring(self.duplicate_of.obj2href(ar))
     return html
Example #26
0
 def before_build(self, bm, elem):
     # if not elem.execution_date:
     #     raise Warning(_("You must specify an execution date"))
     acc = elem.journal.sepa_account
     if not acc:
         raise Warning(
             _("Journal {} has no SEPA account").format(elem.journal))
     if not acc.bic:
         raise Warning(
             _("SEPA account for journal {} has no BIC").format(
                 elem.journal))
     
     return super(WritePaymentsInitiation, self).before_build(bm, elem)
Example #27
0
 def run_from_ui(self, ar, **kw):
     pv = ar.action_param_values
     user = auth.authenticate(
         ar.request, username=pv.username, password=pv.password)
     if user is None:
         ar.error(_("Failed to log in as {}.".format(pv.username)))
     else:
         # user.is_authenticated:
         auth.login(ar.request, user)
         ar.success(
             _("Now logged in as {}").format(user),
             close_window=True,
             goto_url=ar.renderer.plugin.build_plain_url())
Example #28
0
    def run_from_ui(self, ar, **kw):
        obj = ar.selected_rows[0]
        assert obj is None

        def ok(ar):
            for obj in ar:
                obj.state = EnrolmentStates.confirmed
                obj.save()
                ar.set_response(refresh_all=True)

        msg = _(
            "This will confirm all %d enrolments in this list.") % ar.get_total_count()
        ar.confirm(ok, msg, _("Are you sure?"))
Example #29
0
    def run_from_ui(self, ar, **kw):

        def ok(ar2):
            now = timezone.now()
            for obj in self.get_sessions(ar):
                self.end_session(ar, obj, now)
            ar2.set_response(refresh=True)

        if True:
            ok(ar)
        else:
            msg = _("Close {0} sessions.").format(len(ar.selected_rows))
            ar.confirm(ok, msg, _("Are you sure?"))
Example #30
0
 def setup_parameters(cls, fields):
     fields.update(
         ticket_user=dd.ForeignKey(
             settings.SITE.user_model,
             verbose_name=_("Author"),
             blank=True, null=True,
             help_text=_(
                 "Only rows on votables whose author is this user.")),
         exclude_ticket_user=dd.ForeignKey(
             settings.SITE.user_model,
             verbose_name=_("Exclude author"),
             blank=True, null=True,
             help_text=_(
                 "Only rows on votables whose author is not this user.")),
         # show_todo=dd.YesNo.field(_("To do"), blank=True),
         # vote_view=VoteViews.field(blank=True),
         state=VoteStates.field(
             blank=True, help_text=_("Only rows having this state.")),
         mail_mode=MailModes.field(
             blank=True, help_text=_("Only rows having this mail mode.")))
     fld = config.votable_model._meta.get_field('state')
     hlp = lazy_format(
         _("Only rows whose {model} has this state."),
         model=config.votable_model._meta.verbose_name)
     lbl = lazy_format(
         _("{model} state"),
         model=config.votable_model._meta.verbose_name)
     fields.update(
         votable_state=fld.choicelist.field(
             lbl, blank=True, help_text=hlp))
     super(Vote, cls).setup_parameters(fields)
Example #31
0
 class Meta:
     app_label = 'finan'
     abstract = dd.is_abstract_model(__name__, 'PaymentOrder')
     verbose_name = _("Payment Order")
     verbose_name_plural = _("Payment Orders")
Example #32
0
 class Meta:
     app_label = 'ledger'
     verbose_name = _("Match rule")
     verbose_name_plural = _("Match rules")
     unique_together = ['account', 'journal']
Example #33
0
 class Meta:
     app_label = 'finan'
     verbose_name = _("Journal Entry item")
     verbose_name_plural = _("Journal Entry items")
Example #34
0
 class Meta:
     app_label = 'cal'
     verbose_name = _("Task")
     verbose_name_plural = _("Tasks")
     abstract = dd.is_abstract_model(__name__, 'Task')
Example #35
0
 class Meta:
     app_label = 'finan'
     abstract = dd.is_abstract_model(__name__, 'JournalEntry')
     verbose_name = _("Journal Entry")
     verbose_name_plural = _("Journal Entries")
Example #36
0
 class Meta:
     app_label = 'cal'
     abstract = dd.is_abstract_model(__name__, 'Event')
     # abstract = True
     verbose_name = _("Calendar entry")
     verbose_name_plural = _("Calendar entries")
Example #37
0
 class Meta:
     app_label = 'cal'
     verbose_name = _("Recurring event")
     verbose_name_plural = _("Recurring events")
     abstract = dd.is_abstract_model(__name__, 'RecurrentEvent')
Example #38
0
 class Meta:
     app_label = 'ledger'
     verbose_name = _("Payment term")
     verbose_name_plural = _("Payment terms")
Example #39
0
 class Meta:
     verbose_name = _("Account")
     verbose_name_plural = _("Accounts")
     ordering = ['ref']
     app_label = 'ledger'
Example #40
0
 class Meta:
     app_label = 'ledger'
     verbose_name = _("Accounting period")
     verbose_name_plural = _("Accounting periods")
     ordering = ['ref']
Example #41
0
 class Meta:
     app_label = 'ledger'
     verbose_name = _("Fiscal year")
     verbose_name_plural = _("Fiscal years")
     ordering = ['ref']
Example #42
0
class AccountingPeriod(DateRange, Referrable):
    class Meta:
        app_label = 'ledger'
        verbose_name = _("Accounting period")
        verbose_name_plural = _("Accounting periods")
        ordering = ['ref']

    preferred_foreignkey_width = 10

    state = PeriodStates.field(default='open')
    year = dd.ForeignKey('ledger.FiscalYear', blank=True, null=True)
    remark = models.CharField(_("Remark"), max_length=250, blank=True)

    @classmethod
    def get_available_periods(cls, entry_date):
        """Return a queryset of periods available for booking."""
        if entry_date is None:  # added 20160531
            entry_date = dd.today()
        fkw = dict(start_date__lte=entry_date, end_date__gte=entry_date)
        return rt.models.ledger.AccountingPeriod.objects.filter(**fkw)

    @classmethod
    def get_ref_for_date(cls, d):
        """Return a text to be used as :attr:`ref` for a new period.

        Alternative implementation for usage on a site with movements
        before year 2000::

            @classmethod
            def get_ref_for_date(cls, d):
                if d.year < 2000:
                    y = str(d.year - 1900)
                elif d.year < 2010:
                    y = "A" + str(d.year - 2000)
                elif d.year < 2020:
                    y = "B" + str(d.year - 2010)
                elif d.year < 2030:
                    y = "C" + str(d.year - 2020)
                return y + "{:0>2}".format(d.month)

        """
        y = FiscalYear.year2ref(d.year)
        return "{}-{:0>2}".format(y, d.month)

        # if dd.plugins.ledger.fix_y2k:
        #     return rt.models.ledger.FiscalYear.from_int(d.year).ref \
        #         + "{:0>2}".format(d.month)

        # return "{0.year}-{0.month:0>2}".format(d)

        # """The template used for building the :attr:`ref` of an
        # :class:`AccountingPeriod`.
        #
        # `Format String Syntax
        # <https://docs.python.org/2/library/string.html#formatstrings>`_
        #
        # """

    @classmethod
    def get_periods_in_range(cls, p1, p2):
        return cls.objects.filter(ref__gte=p1.ref, ref__lte=p2.ref)

    @classmethod
    def get_period_filter(cls, voucher_prefix, p1, p2, **kwargs):
        if p1 is None:
            return kwargs

        # ignore preliminary movements if a start_period is given:
        kwargs[voucher_prefix + "journal__preliminary"] = False

        accounting_period = voucher_prefix + "accounting_period"

        if p2 is None:
            kwargs[accounting_period] = p1
        else:
            periods = cls.get_periods_in_range(p1, p2)
            kwargs[accounting_period + '__in'] = periods
        return kwargs

    @classmethod
    def get_default_for_date(cls, d):
        ref = cls.get_ref_for_date(d)
        obj = rt.models.ledger.AccountingPeriod.get_by_ref(ref, None)
        if obj is None:
            values = dict(start_date=d.replace(day=1))
            values.update(end_date=last_day_of_month(d))
            values.update(ref=ref)
            obj = AccountingPeriod(**values)
            obj.full_clean()
            obj.save()
        return obj

    def full_clean(self, *args, **kwargs):
        if self.start_date is None:
            self.start_date = dd.today().replace(day=1)
        if not self.year:
            self.year = FiscalYear.get_or_create_from_date(self.start_date)
        super(AccountingPeriod, self).full_clean(*args, **kwargs)

    def __str__(self):
        if not self.ref:
            return dd.obj2str(self)
            # "{0} {1} (#{0})".format(self.pk, self.year)
        return self.ref
Example #43
0
 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" % str(doc))
Example #44
0
class VoucherChecker(Checker):
    verbose_name = _("Check integrity of ledger vouchers")
    messages = dict(
        missing=_("Missing movement {0}."),
        unexpected=_("Unexpected movement {0}."),
        diff=_("Movement {0} : {1}"),
        warning=_("Failed to get movements for {0} : {1}"),
    )

    def get_checkable_models(self):
        return rt.models_by_base(Voucher)  # also MTI children

    def get_checkdata_problems(self, obj, fix=False):
        if obj.__class__ is rt.models.ledger.Voucher:
            if obj.get_mti_leaf() is None:
                yield (True, _("Voucher without MTI leaf"))
                if fix:
                    obj.movement_set.all().delete()
                    obj.delete()
            return

        def m2k(obj):
            return obj.seqno

        wanted = dict()
        # if obj.state in dd.plugins.ledger.registered_states:
        if not obj.state.is_editable:
            seqno = 0
            fcu = dd.plugins.ledger.suppress_movements_until
            try:
                for m in obj.get_wanted_movements():
                    if fcu and m.value_date <= fcu:
                        continue
                    seqno += 1
                    m.seqno = seqno
                    # if fcu and m.value_date <= fcu:
                    #     m.cleared = True
                    m.full_clean()
                    wanted[m2k(m)] = m
            except Warning as e:
                yield (False, self.messages['warning'].format(obj, e))
                return

        for em in obj.movement_set.order_by('seqno'):
            wm = wanted.pop(m2k(em), None)
            if wm is None:
                yield (False, self.messages['unexpected'].format(em))
                return
            diffs = []
            for k in ('partner_id', 'account_id', 'dc', 'amount',
                      'value_date'):
                emv = getattr(em, k)
                wmv = getattr(wm, k)
                if emv != wmv:
                    diffs.append(u"{} ({}!={})".format(k, emv, wmv))
            if len(diffs) > 0:
                yield (False,
                       self.messages['diff'].format(em, u', '.join(diffs)))
                return

        if wanted:
            for missing in wanted.values():
                yield (False, self.messages['missing'].format(missing))
                return
Example #45
0
 class Meta:
     app_label = 'finan'
     abstract = dd.is_abstract_model(__name__, 'BankStatement')
     verbose_name = _("Bank Statement")
     verbose_name_plural = _("Bank Statements")
Example #46
0
File: std.py Project: einarfelix/xl
def objects():

    DPR = rt.models.calview.DailyPlannerRow
    yield DPR(end_time="12:00", **dd.str2kw('designation', _("AM")))
    yield DPR(start_time="12:00", **dd.str2kw('designation', _("PM")))
    yield DPR(**dd.str2kw('designation', _("All day")))
Example #47
0
 class Meta:
     app_label = 'cal'
     abstract = dd.is_abstract_model(__name__, 'RemoteCalendar')
     verbose_name = _("Remote Calendar")
     verbose_name_plural = _("Remote Calendars")
     ordering = ['seqno']
Example #48
0
class Plugin(ad.Plugin):
    verbose_name = _("Virtual tables")

    def setup_main_menu(self, site, profile, m):
        m = m.add_menu(self.app_label, self.verbose_name)
        m.add_action('vtables.CitiesAndInhabitants')
Example #49
0
class Event(Component, Ended, Assignable, TypedPrintable, Mailable, Postable,
            Publishable):
    class Meta:
        app_label = 'cal'
        abstract = dd.is_abstract_model(__name__, 'Event')
        # abstract = True
        verbose_name = _("Calendar entry")
        verbose_name_plural = _("Calendar entries")
        # verbose_name = pgettext("cal", "Event")
        # verbose_name_plural = pgettext("cal", "Events")

    publisher_location = "cal"
    publisher_template = "cal/event.pub.html"

    update_guests = UpdateGuests()
    update_events = UpdateEntriesByEvent()
    show_today = ShowEntriesByDay('start_date')

    event_type = dd.ForeignKey('cal.EventType', blank=True, null=True)

    transparent = models.BooleanField(_("Transparent"), default=False)
    room = dd.ForeignKey('cal.Room', null=True, blank=True)
    # priority = dd.ForeignKey(Priority, null=True, blank=True)
    state = EntryStates.field(default=EntryStates.as_callable('suggested'))
    all_day = ExtAllDayField(_("all day"))

    move_next = MoveEntryNext()

    show_conflicting = dd.ShowSlaveTable(ConflictingEvents)
    allow_merge_action = False

    @classmethod
    def setup_parameters(cls, params):
        params.update(
            mixins.ObservedDateRange(
                user=dd.ForeignKey(settings.SITE.user_model,
                                   verbose_name=_("Managed by"),
                                   blank=True,
                                   null=True),
                event_type=dd.ForeignKey('cal.EventType',
                                         blank=True,
                                         null=True),
                room=dd.ForeignKey('cal.Room', blank=True, null=True),
                project=dd.ForeignKey(settings.SITE.project_model,
                                      blank=True,
                                      null=True),
                assigned_to=dd.ForeignKey(settings.SITE.user_model,
                                          verbose_name=_("Assigned to"),
                                          blank=True,
                                          null=True),
                state=EntryStates.field(blank=True),
                # unclear = models.BooleanField(_("Unclear events"))
                observed_event=EventEvents.field(blank=True),
                show_appointments=dd.YesNo.field(_("Appointments"),
                                                 blank=True),
                presence_guest=dd.ForeignKey(dd.plugins.cal.partner_model,
                                             verbose_name=_("Guest"),
                                             blank=True,
                                             null=True)))
        if settings.SITE.project_model:
            params['project'].help_text = format_lazy(
                _("Show only entries having this {project}."),
                project=settings.SITE.project_model._meta.verbose_name)
        return params

    # params_layout = """
    # start_date end_date observed_event state
    # user assigned_to project event_type room show_appointments
    # """

    # cal_params_layout = """user event_type room project presence_guest"""

    @classmethod
    def calendar_param_filter(cls, qs, pv):
        if pv.user:
            qs = qs.filter(user=pv.user)
        if pv.assigned_to:
            qs = qs.filter(assigned_to=pv.assigned_to)

        if settings.SITE.project_model is not None and pv.project:
            qs = qs.filter(project=pv.project)

        if pv.event_type:
            qs = qs.filter(event_type=pv.event_type)
        else:
            if pv.show_appointments == dd.YesNo.yes:
                qs = qs.filter(event_type__is_appointment=True)
            elif pv.show_appointments == dd.YesNo.no:
                qs = qs.filter(event_type__is_appointment=False)

        if pv.state:
            qs = qs.filter(state=pv.state)

        if pv.room:
            qs = qs.filter(room=pv.room)

        if pv.observed_event == EventEvents.stable:
            qs = qs.filter(state__in=set(EntryStates.filter(fixed=True)))
        elif pv.observed_event == EventEvents.pending:
            qs = qs.filter(state__in=set(EntryStates.filter(fixed=False)))

        # multi-day entries should appear on each day
        if pv.start_date:
            c1 = Q(start_date__gte=pv.start_date)
            c2 = Q(start_date__lt=pv.start_date,
                   end_date__isnull=False,
                   end_date__gte=pv.start_date)
            qs = qs.filter(c1 | c2)
        if pv.end_date:
            c1 = Q(end_date__isnull=True, start_date__lte=pv.end_date)
            c2 = Q(end_date__isnull=False, end_date__lte=pv.end_date)
            qs = qs.filter(c1 | c2)

            qs = qs.filter(
                Q(end_date__isnull=True, start_date__lte=pv.end_date)
                | Q(end_date__gte=pv.end_date))
        if pv.presence_guest:
            qs = qs.filter(
                Q(guest__partner=pv.presence_guest)
                | Q(event_type__all_rooms=True))
        return qs

    def strftime(self):
        if not self.start_date:
            return ''
        d = self.start_date.strftime(settings.SITE.date_format_strftime)
        if self.start_time:
            t = self.start_time.strftime(settings.SITE.time_format_strftime)
            return "%s %s" % (d, t)
        else:
            return d

    def get_diplay_color(self):
        if self.room:
            return self.room.display_color

    def calendar_fmt(self, pv):
        # if pv.user:
        # if pv.assigned_to:
        # if settings.SITE.project_model is not None and pv.project:
        # if pv.event_type:
        t = []
        if self.start_time:
            t.append(str(self.start_time)[:5])
        # elif not pv.start_date:
        # t.append(str(self.start_date))
        if not pv.user and self.user:
            t.append(str(self.user))
        if self.summary:
            t.append(self.summary)
        if not pv.event_type and self.event_type:
            t.append(str(self.event_type))
        if not pv.room and self.room:
            t.append(str(self.room))
        if settings.SITE.project_model is not None and not pv.project and self.project:
            t.append(str(self.project))

        # if u is None:
        #     return "{} {}".format(t, self.room) if self.room else t
        # u = u.initials or u.username or str(u)
        return E.span(" ".join(t))
        # return "{} {}".format(t, u)

    def colored_calendar_fmt(self, pv):
        ele = E.span(self.calendar_fmt(pv))
        data_color = self.get_diplay_color()
        if data_color:
            dot = E.span(u"\u00A0", CLASS="dot")
            # ele.attrib['style'] = "color: white;background-color: {};".format(data_color)
            dot.attrib['style'] = "background-color: {};".format(data_color)
            return E.div(*[dot, ele])
        else:
            return E.div(*[ele])

    def __str__(self):
        if self.summary:
            s = self.summary
        elif self.event_type:
            s = str(self.event_type)
        elif self.pk:
            s = self._meta.verbose_name + " #" + str(self.pk)
        else:
            s = _("Unsaved %s") % self._meta.verbose_name
        when = self.strftime()
        if when:
            s = "{} ({})".format(s, when)
        # u = self.user
        # if u is None and self.room:
        #     u = self.room
        # if u is None:
        #     return '%s object (%s)' % (self.__class__.__name__, self.pk)
        # u = u.initials or u.username or str(u)
        # s = "{} ({})".format(s, u)
        return s
        # if e.start_time:
        #     t = str(e.start_time)[:5]
        # else:
        #     t = str(e.event_type)
        # u = e.user
        # if u is None:
        #     return "{} {}".format(t, e.room) if e.room else t
        # u = u.initials or u.username or str(u)
        # return "{} {}".format(t, u)

    def duration_veto(obj):
        if obj.end_date is None:
            return
        et = obj.event_type
        if et is None:
            return
        duration = obj.end_date - obj.start_date
        # print (20161222, duration.days, et.max_days)
        if et.max_days and duration.days > et.max_days:
            return _("Event lasts {0} days but only {1} are allowed.").format(
                duration.days, et.max_days)

    def full_clean(self, *args, **kw):
        super(Event, self).full_clean(*args, **kw)
        et = self.event_type
        if et is not None and et.default_duration is not None:
            assert isinstance(et.default_duration, Duration)
            dt = self.get_datetime('start')
            if dt is not None and self.end_time is None:
                self.set_datetime('end', dt + et.default_duration)
            else:
                dt = self.get_datetime('end')
                if dt is not None and self.start_time is None:
                    self.set_datetime('start', dt - et.default_duration)
        if et and et.max_days == 1:
            # avoid "Abandoning with 297 unsaved instances" when migrating data
            # that was created before the current rules
            self.end_date = None
        msg = self.duration_veto()
        if msg is not None:
            raise ValidationError(str(msg))

    def start_time_changed(self, ar):
        et = self.event_type
        start_time = self.get_datetime('start')
        if start_time is not None \
                and et is not None and et.default_duration is not None:
            dt = start_time + et.default_duration
            self.set_datetime('end', dt)
            # self.end_time = str(self.start_time + et.default_duration)

    # removed because this behaviour is irritating
    # def end_time_changed(self, ar):
    #     et = self.event_type
    #     end_time = self.get_datetime('end', 'start')
    #     if end_time is not None \
    #             and et is not None and et.default_duration is not None:
    #         dt = end_time - et.default_duration
    #         self.set_datetime('start', dt)
    #         # self.start_time = str(self.end_time - et.default_duration)

    def get_change_observers(self, ar=None):
        # implements ChangeNotifier
        if not self.is_user_modified():
            return
        for x in super(Event, self).get_change_observers(ar):
            yield x
        for u in (self.user, self.assigned_to):
            if u is not None:
                yield (u, u.mail_mode)

    def has_conflicting_events(self):
        qs = self.get_conflicting_events()
        if qs is None:
            return False
        if self.event_type is not None:
            if self.event_type.transparent:
                return False
            # holidays (all room events) conflict also with events
            # whose type otherwise would allow conflicting events
            if qs.filter(event_type__all_rooms=True).count() > 0:
                return True
            n = self.event_type.max_conflicting - 1
        else:
            n = 0
        # date = self.start_date
        # if date.day == 9 and date.month == 3:
        #     dd.logger.info("20171130 has_conflicting_events() %s", qs.query)
        return qs.count() > n

    def get_conflicting_events(self):
        if self.transparent:
            return
        # if self.event_type is not None and self.event_type.transparent:
        #     return
        # return False
        # Event = dd.resolve_model('cal.Event')
        # ot = ContentType.objects.get_for_model(RecurrentEvent)
        qs = self.__class__.objects.filter(transparent=False)
        qs = qs.exclude(event_type__transparent=True)

        # if self.state.transparent:
        #     # cancelled entries are basically transparent to all
        #     # others. Except if they have an owner, in which case we
        #     # wouldn't want Lino to put another automatic entry at
        #     # that date.
        #     if self.owner_id is None:
        #         return
        #     qs = qs.filter(
        #         owner_id=self.owner_id, owner_type=self.owner_type)

        end_date = self.end_date or self.start_date
        flt = Q(start_date=self.start_date, end_date__isnull=True)
        flt |= Q(end_date__isnull=False,
                 start_date__lte=self.start_date,
                 end_date__gte=end_date)
        if end_date == self.start_date:
            if self.start_time and self.end_time:
                # the other starts before me and ends after i started
                c1 = Q(start_time__lte=self.start_time,
                       end_time__gt=self.start_time)
                # the other ends after me and started before i ended
                c2 = Q(end_time__gte=self.end_time,
                       start_time__lt=self.end_time)
                # the other is full day
                c3 = Q(end_time__isnull=True, start_time__isnull=True)
                flt &= (c1 | c2 | c3)
        qs = qs.filter(flt)

        # saved events don't conflict with themselves:
        if self.id is not None:
            qs = qs.exclude(id=self.id)

        # automatic events never conflict with other generated events
        # of same owner. Rule needed for update_events.
        if self.auto_type:
            qs = qs.exclude(
                # auto_type=self.auto_type,
                auto_type__isnull=False,
                owner_id=self.owner_id,
                owner_type=self.owner_type)

        # transparent events (cancelled or omitted) usually don't
        # cause a conflict with other events (e.g. a holiday). But a
        # cancelled course lesson should not tolerate another lesson
        # of the same course on the same date.
        ntstates = EntryStates.filter(transparent=False)
        if self.owner_id is None:
            if self.state.transparent:
                return
            qs = qs.filter(state__in=ntstates)
        else:
            if self.state.transparent:
                qs = qs.filter(owner_id=self.owner_id,
                               owner_type=self.owner_type)
            else:
                qs = qs.filter(
                    Q(state__in=ntstates)
                    | Q(owner_id=self.owner_id, owner_type=self.owner_type))

        if self.room is None:
            # an entry that needs a room but doesn't yet have one,
            # conflicts with any all-room entry (e.g. a holiday).  For
            # generated entries this list extends to roomed entries of
            # the same generator.

            if self.event_type is None or not self.event_type.all_rooms:
                if self.owner_id is None:
                    qs = qs.filter(event_type__all_rooms=True)
                else:
                    qs = qs.filter(
                        Q(event_type__all_rooms=True)
                        | Q(owner_id=self.owner_id, owner_type=self.owner_type)
                    )
        else:
            # other event in the same room
            c1 = Q(room=self.room)
            # other event locks all rooms (e.g. holidays)
            # c2 = Q(room__isnull=False, event_type__all_rooms=True)
            c2 = Q(event_type__all_rooms=True)
            qs = qs.filter(c1 | c2)
        if self.user is not None:
            if self.event_type is not None:
                if self.event_type.locks_user:
                    # c1 = Q(event_type__locks_user=False)
                    # c2 = Q(user=self.user)
                    # qs = qs.filter(c1|c2)
                    qs = qs.filter(user=self.user, event_type__locks_user=True)
        # qs = Event.objects.filter(flt,owner_type=ot)
        # if we.start_date.month == 7:
        # print 20131011, self, we.start_date, qs.count()
        # print 20131025, qs.query
        return qs

    def is_fixed_state(self):
        return self.state.fixed
        # return self.state in EntryStates.editable_states

    def is_user_modified(self):
        return self.state != EntryStates.suggested

    def before_ui_save(self, ar, **kw):
        # logger.info("20130528 before_ui_save")
        if self.state is EntryStates.suggested:
            self.state = EntryStates.draft
        super(Event, self).before_ui_save(ar, **kw)

    def on_create(self, ar):
        if self.event_type is None:
            self.event_type = ar.user.event_type or \
                settings.SITE.site_config.default_event_type
        self.start_date = self.start_date or settings.SITE.today()
        self.start_time = timezone.now().time()
        # see also Assignable.on_create()
        super(Event, self).on_create(ar)
        if not settings.SITE.loading_from_dump:
            # print("20190328 before_ui_save", self.is_user_modified())
            if isinstance(self.owner, RecurrenceSet):
                self.owner.before_auto_event_save(self)
            if isinstance(self.owner, EventGenerator):
                self.event_type = self.owner.update_cal_event_type()

    # def on_create(self,ar):
    # self.start_date = settings.SITE.today()
    # self.start_time = datetime.datetime.now().time()
    # ~ # default user is almost the same as for UserAuthored
    # ~ # but we take the *real* user, not the "working as"
    # if self.user_id is None:
    # u = ar.user
    # if u is not None:
    # self.user = u
    # super(Event,self).on_create(ar)

    def after_ui_save(self, ar, cw):
        super(Event, self).after_ui_save(ar, cw)
        self.update_guests.run_from_code(ar)

    def before_state_change(self, ar, old, new):
        super(Event, self).before_state_change(ar, old, new)
        if new.noauto:
            self.auto_type = None
        if new.guest_state and self.event_type_id and self.event_type.force_guest_states:
            for obj in self.guest_set.exclude(state=new.guest_state):
                obj.state = new.guest_state
                obj.full_clean()
                obj.save()

    def can_edit_guests_manually(self):
        if self.state.fill_guests:
            if self.event_type and self.event_type.fill_presences:
                return False
        return True

    def suggest_guests(self):
        done = set()
        for o in (self.owner, self.project):
            if isinstance(o, EventGenerator):
                if o in done:
                    continue
                done.add(o)
                for obj in o.suggest_cal_guests(self):
                    yield obj
        # if self.owner:
        #     for obj in self.owner.suggest_cal_guests(self):
        #         yield obj

    def get_event_summary(event, ar):
        # from django.utils.translation import ugettext as _
        s = event.summary
        # if event.owner_id:
        #     s += " ({0})".format(event.owner)
        if event.user is not None and event.user != ar.get_user():
            if event.access_class == AccessClasses.show_busy:
                s = _("Busy")
            s = event.user.username + ': ' + str(s)
        elif settings.SITE.project_model is not None \
                and event.project is not None:
            s += " " + str(_("with")) + " " + str(event.project)
        if event.state:
            s = ("(%s) " % str(event.state)) + s
        n = event.guest_set.all().count()
        if n:
            s = ("[%d] " % n) + s
        return s

    def get_postable_recipients(self):
        """return or yield a list of Partners"""
        if self.project:
            if isinstance(self.project, dd.plugins.cal.partner_model):
                yield self.project
        for g in self.guest_set.all():
            yield g.partner
        # if self.user.partner:
        # yield self.user.partner

    def get_mailable_type(self):
        return self.event_type

    def get_mailable_recipients(self):
        if self.project:
            if isinstance(self.project, dd.plugins.cal.partner_model):
                yield ('to', self.project)
        for g in self.guest_set.all():
            yield ('to', g.partner)
        if self.user.partner:
            yield ('cc', self.user.partner)

    # def get_mailable_body(self,ar):
    # return self.description

    @dd.displayfield(_("When"), sortable_by=['start_date', 'start_time'])
    def when_text(self, ar):
        txt = when_text(self.start_date, self.start_time)
        if self.end_date and self.end_date != self.start_date:
            txt += "-" + when_text(self.end_date, self.end_time)
        return txt

    @dd.displayfield(_("When"), sortable_by=['start_date', 'start_time'])
    def when_html(self, ar):
        if ar is None:
            return ''
        txt = when_text(self.start_date, self.start_time)
        if False:  # removed 20181106 because it is irritating and
            # nobody uses it.
            return rt.models.cal.EntriesByDay.as_link(ar, self.start_date, txt)
        return self.obj2href(ar, txt)

    @dd.displayfield(_("Link URL"))
    def url(self, ar):
        return 'foo'

    @dd.virtualfield(dd.DisplayField(_("Reminder")))
    def reminder(self, request):
        return False

    # reminder.return_type = dd.DisplayField(_("Reminder"))

    def get_calendar(self):
        # for sub in Subscription.objects.filter(user=ar.get_user()):
        # if sub.contains_event(self):
        # return sub
        return None

    @dd.virtualfield(dd.ForeignKey('cal.Calendar'))
    def calendar(self, ar):
        return self.get_calendar()

    def get_print_language(self):
        # if settings.SITE.project_model is not None and self.project:
        if self.project:
            return self.project.get_print_language()
        if self.user:
            return self.user.language
        return settings.SITE.get_default_language()

    @classmethod
    def get_default_table(cls):
        return OneEvent

    @classmethod
    def on_analyze(cls, lino):
        cls.DISABLED_AUTO_FIELDS = dd.fields_list(cls, "summary")
        super(Event, cls).on_analyze(lino)

    def auto_type_changed(self, ar):
        if self.auto_type:
            self.summary = self.owner.update_cal_summary(
                self.event_type, self.auto_type)
Example #50
0
def warn_jnl_account(jnl):
    fld = jnl._meta.get_field('account')
    raise Warning(_("Field '{0}' in journal '{0}' is empty!").format(
        fld.verbose_name, jnl))
Example #51
0
 class Meta:
     app_label = 'cal'
     verbose_name = _("Recurrency policy")
     verbose_name_plural = _('Recurrency policies')
     abstract = dd.is_abstract_model(__name__, 'EventPolicy')
Example #52
0
class ContactDetailsOwner(Contactable, Phonable):

    class Meta:
        abstract = True

    if dd.is_installed('phones'):

        def after_ui_save(self, ar, cw):
            if cw is None:  # it's a new instance
                for cdt in ContactDetailTypes.get_list_items():
                    self.propagate_contact_detail(cdt)
                pass
            else:
                for k, old, new in cw.get_updates():
                    cdt = ContactDetailTypes.find(field_name=k)
                    # cdt = getattr(ContactDetailTypes, k, False)
                    if cdt:
                        self.propagate_contact_detail(cdt)
            super(ContactDetailsOwner, self).after_ui_save(ar, cw)

        def propagate_contact_detail(self, cdt):
            k = cdt.field_name
            if k:
                value = getattr(self, k)
                ContactDetail = rt.models.phones.ContactDetail
                kw = dict(partner=self, primary=True, detail_type=cdt)
                try:
                    cd = ContactDetail.objects.get(**kw)
                    if value:
                        cd.value = value
                        # don't full_clean() because no need to check
                        # primary of other items
                        cd.save()
                    else:
                        cd.delete()
                except ContactDetail.DoesNotExist:
                    if value:
                        kw.update(value=value)
                        cd = ContactDetail(**kw)
                        # self.phones_by_partner.add(cd, bulk=False)
                        cd.save()

        def propagate_contact_details(self, ar=None):
            watcher = ChangeWatcher(self)
            for cdt in ContactDetailTypes.get_list_items():
                self.propagate_contact_detail(cdt)
            if ar is not None:
                watcher.send_update(ar)

        def get_overview_elems(self, ar):
            # elems = super(ContactDetailsOwner, self).get_overview_elems(ar)
            yield rt.models.phones.ContactDetailsByPartner.get_table_summary(
                self, ar)

        @dd.displayfield(_("Contact details"))
        def contact_details(self, ar):
            if ar is None:
                return ''
            sar = rt.models.phones.ContactDetailsByPartner.request(parent=ar, master_instance=self)
            items = [o.detail_type.as_html(o, sar)
                     for o in sar if not o.end_date]
            return E.p(*join_elems(items, sep=', '))



    else:

        def get_overview_elems(self, ar):
            return []

        @dd.displayfield(_("Contact details"))
        def contact_details(self, ar):
            # if ar is None:
            #     return ''
            items = []
            for cdt in ContactDetailTypes.get_list_items():
                if cdt.field_name:
                    value = getattr(self, cdt.field_name)
                if value:
                    items.append(cdt.format(value))
            # items.append(ContactDetailTypes.email.format(self.email))
            # # items.append(E.a(self.email, href="mailto:" + self.email))
            # items.append(self.phone)
            # items.append(E.a(self.url, href=self.url))
            return E.p(*join_elems(items, sep=', '))
Example #53
0
class PaymentOrder(FinancialVoucher, Printable):
    class Meta:
        app_label = 'finan'
        abstract = dd.is_abstract_model(__name__, 'PaymentOrder')
        verbose_name = _("Payment Order")
        verbose_name_plural = _("Payment Orders")

    total = dd.PriceField(_("Total"), blank=True, null=True)
    execution_date = models.DateField(
        _("Execution date"), blank=True, null=True)

    # show_items = dd.ShowSlaveTable('finan.ItemsByPaymentOrder')
    write_xml = WritePaymentsInitiation()

    @dd.displayfield(_("Print"))
    def print_actions(self, ar):
        if ar is None:
            return ''
        elems = []
        elems.append(ar.instance_action_button(
            self.write_xml))
        return E.p(*join_elems(elems, sep=", "))

    def get_wanted_movements(self):
        """Implements
        :meth:`lino_xl.lib.ledger.Voucher.get_wanted_movements`
        for payment orders.

        As a side effect this also computes the :attr:`total` field and saves
        the voucher.

        """
        # dd.logger.info("20151211 cosi.PaymentOrder.get_wanted_movements()")
        acc = self.journal.account
        if not acc:
            warn_jnl_account(self.journal)
        # TODO: what if the needs_partner of the journal's account
        # is not checked? Shouldn't we raise an error here?
        amount, movements_and_items = self.get_finan_movements()
        if abs(amount > MAX_AMOUNT):
            dd.logger.warning("Oops, %s is too big", amount)
            return
        self.total = - amount
        item_partner = self.journal.partner is None
        for m, i in movements_and_items:
            yield m
            if item_partner:
                yield self.create_movement(
                    i, (acc, None), m.project, not m.dc, m.amount,
                    partner=m.partner, match=i.get_match())
        if not item_partner:
            yield self.create_movement(
                None, (acc, None), None, not self.journal.dc, amount,
                partner=self.journal.partner, match=self)
                # 20191226 partner=self.journal.partner, match=self.get_default_match())

        # side effect!:
        self.full_clean()
        self.save()

    def add_item_from_due(self, obj, **kwargs):
        # if obj.bank_account is None:
        #     return
        i = super(PaymentOrder, self).add_item_from_due(obj, **kwargs)
        i.bank_account = obj.bank_account
        return i
Example #54
0
 class Meta:
     app_label = 'finan'
     verbose_name = _("Bank Statement item")
     verbose_name_plural = _("Bank Statement items")
Example #55
0
class Voucher(UserAuthored, Duplicable, UploadController,
              PeriodRangeObservable):
    # class Voucher(UserAuthored, Duplicable, Registrable, UploadController, PeriodRangeObservable):

    class Meta:
        # abstract = True
        verbose_name = _("Voucher")
        verbose_name_plural = _("Vouchers")
        app_label = 'ledger'

    manager_roles_required = dd.login_required(VoucherSupervisor)
    # workflow_state_field = 'state'

    journal = JournalRef()
    entry_date = models.DateField(_("Entry date"))
    voucher_date = models.DateField(_("Voucher date"))
    accounting_period = dd.ForeignKey('ledger.AccountingPeriod', blank=True)
    number = VoucherNumber(_("No."), blank=True, null=True)
    narration = models.CharField(_("Narration"), max_length=200, blank=True)

    # state = VoucherStates.field(default='draft')

    def unused_get_partner(self):
        # return None
        raise NotImplementedError("{} must define get_partner()".format(
            self.__class__))

    def unused_get_wanted_movements(self):
        # deactivated this because MRO is complex, see 20200128
        # return []
        raise NotImplementedError(
            "{} must define get_wanted_movements()".format(self.__class__))

    @property
    def currency(self):
        """This is currently used only in some print templates.

        """
        return dd.plugins.ledger.currency_symbol

    @dd.displayfield(_("No."))
    def number_with_year(self, ar):
        return "{0}/{1}".format(self.number, self.accounting_period.year)

    @classmethod
    def quick_search_filter(model, search_text, prefix=''):
        """Overrides :meth:`lino.core.model.Model.quick_search_filter`.

        Examples:

        123 -> voucher number 123 in current year

        123/2014 -> voucher number 123 in 2014

        """
        # logger.info(
        #     "20160612 Voucher.quick_search_filter(%s, %r, %r)",
        #     model, search_text, prefix)
        parts = search_text.split('/')
        if len(parts) == 2:
            kw = {
                prefix + 'number': parts[0],
                prefix + 'accounting_period__year': parts[1]
            }
            return models.Q(**kw)
        if search_text.isdigit() and not search_text.startswith('0'):
            kw = {
                prefix + 'number':
                int(search_text),
                prefix + 'accounting_period__year':
                FiscalYear.get_or_create_from_date(dd.today())
            }
            return models.Q(**kw)
        return super(Voucher, model).quick_search_filter(search_text, prefix)

    def __str__(self):
        if self.number is None:
            return "{0}#{1}".format(self.journal.ref, self.id)
        # moved to implementing subclasses:
        #   if self.state not in dd.plugins.ledger.registered_states:
        #     # raise Exception("20191223 {} is not in {}".format(self.state, dd.plugins.ledger.registered_states))
        assert self.number is not None
        if self.journal.yearly_numbering:
            return "{0} {1}/{2}".format(self.journal.ref, self.number,
                                        self.accounting_period.year)
        return "{0} {1}".format(self.journal.ref, self.number)
        # if self.journal.ref:
        #     return "%s %s" % (self.journal.ref,self.number)
        # return "#%s (%s %s)" % (self.number,self.journal,self.year)

    def full_clean(self, *args, **kwargs):
        if self.entry_date is None:
            self.entry_date = dd.today()
        if self.voucher_date is None:
            self.voucher_date = self.entry_date
        if not self.accounting_period_id:
            self.accounting_period = AccountingPeriod.get_default_for_date(
                self.entry_date)
        if self.number is None:
            self.number = self.journal.get_next_number(self)
        super(Voucher, self).full_clean(*args, **kwargs)

    def on_create(self, ar):
        super(Voucher, self).on_create(ar)
        if self.entry_date is None:
            if ar is None:
                self.entry_date = dd.today()
            else:
                info = LedgerInfo.get_for_user(ar.get_user())
                self.entry_date = info.entry_date or dd.today()

    def on_duplicate(self, ar, master):
        self.number = self.entry_date = self.accounting_period = None
        self.on_create(ar)
        super(Voucher, self).on_duplicate(ar, master)

    def entry_date_changed(self, ar):
        self.accounting_period = AccountingPeriod.get_default_for_date(
            self.entry_date)
        self.voucher_date = self.entry_date
        self.accounting_period_changed(ar)
        info = LedgerInfo.get_for_user(ar.get_user())
        info.entry_date = self.entry_date
        info.full_clean()
        info.save()

    def accounting_period_changed(self, ar):
        self.number = self.journal.get_next_number(self)

    def get_detail_action(self, ar):
        """Custom :meth:`get_detail_action
        <lino.core.model.Model.get_detail_action>` because the
        detail_layout to use depends on the journal's voucher type.

        """
        if self.journal_id:
            table = self.journal.voucher_type.table_class
            if table:
                ba = table.detail_action
                ba = ba.action.defining_actor.detail_action
                # if ar is None or ba.get_row_permission(ar, self, None):
                #     return ba
                if ar is None or ba.get_view_permission(
                        ar.get_user().user_type):
                    return ba
                return None
        return super(Voucher, self).get_detail_action(ar)

    def get_due_date(self):
        return self.entry_date

    def get_trade_type(self):
        return self.journal.trade_type

    def get_printed_name(self):
        return dd.babelattr(self.journal, 'printed_name')

    def get_movement_description(self, mvt, ar=None):
        if ar is None:
            return
        if self.narration:
            yield self.narration
        p = self.get_partner()
        if p is not None and p != ar.master_instance:
            yield ar.obj2html(p)
        if mvt.partner and mvt.partner != p:
            yield ar.obj2html(mvt.partner)

    def after_ui_save(self, ar, cw):
        super(Voucher, self).after_ui_save(ar, cw)
        p = self.get_partner()
        if p is None:
            return
        tt = self.get_trade_type()
        account = tt.get_partner_invoice_account(p)
        if account is None:
            return
        if self.items.exists():
            return
        i = self.add_voucher_item(account=account)
        i.full_clean()
        i.save()

    @classmethod
    def get_journals(cls):
        vt = VoucherTypes.get_for_model(cls)
        return Journal.objects.filter(voucher_type=vt).order_by('seqno')

    @dd.chooser()
    def unused_accounting_period_choices(cls, entry_date):
        # deactivated because it also limits the choices of the
        # parameter field (which is a Lino bug)
        return rt.models.ledger.AccountingPeriod.get_available_periods(
            entry_date)

    @dd.chooser()
    def journal_choices(cls):
        # logger.info("20140603 journal_choices %r", cls)
        return cls.get_journals()

    @classmethod
    def create_journal(cls, trade_type=None, account=None, **kw):
        vt = VoucherTypes.get_for_model(cls)
        if isinstance(trade_type, str):
            trade_type = TradeTypes.get_by_name(trade_type)
        if isinstance(account, str):
            account = rt.models.ledger.Account.get_by_ref(account)
        if account is not None:
            kw.update(account=account)
        return Journal(trade_type=trade_type, voucher_type=vt, **kw)

    # def get_default_match(self): removed 20191226
    #     return str(self)
    # return "%s#%s" % (self.journal.ref, self.id)
    # return "%s%s" % (self.id, self.journal.ref)

    # def get_voucher_match(self):
    #     return str(self)  # "{0}{1}".format(self.journal.ref, self.number)

    def do_and_clear(self, func, do_clear):
        existing_mvts = self.movement_set.all()
        partners = set()
        # accounts = set()
        if not self.journal.auto_check_clearings:
            do_clear = False
        if do_clear:
            for m in existing_mvts.filter(account__clearable=True,
                                          partner__isnull=False):
                partners.add(m.partner)
        existing_mvts.delete()
        func(partners)
        if do_clear:
            for p in partners:
                check_clearings_by_partner(p)
            # for a in accounts:
            #     check_clearings_by_account(a)

        # dd.logger.info("20151211 Done cosi.Voucher.register_voucher()")

    def disable_delete(self, ar=None):
        msg = self.journal.disable_voucher_delete(self)
        if msg is not None:
            return msg
        return super(Voucher, self).disable_delete(ar)

    def create_movement(self, item, acc_tuple, project, dc, amount, **kw):
        # dd.logger.info("20151211 ledger.create_movement()")
        account, ana_account = acc_tuple
        if account is None and item is not None:
            raise Warning("No account specified for {}".format(item))
        if not isinstance(account, rt.models.ledger.Account):
            raise Warning("{} is not an Account object".format(account))
        kw['voucher'] = self
        kw['account'] = account
        if ana_account is not None:
            kw['ana_account'] = ana_account
        kw['value_date'] = self.entry_date
        if account.clearable:
            kw.update(cleared=False)
        else:
            kw.update(cleared=True)

        if dd.plugins.ledger.project_model:
            kw['project'] = project

        if amount < 0:
            amount = -amount
            dc = not dc
        kw['amount'] = amount
        kw['dc'] = dc

        b = rt.models.ledger.Movement(**kw)
        return b

    def get_mti_leaf(self):
        return mti.get_child(self, self.journal.voucher_type.model)

    # def obj2html(self, ar):
    def obj2href(self, ar):
        return ar.obj2html(self.get_mti_leaf())

    #~ def add_voucher_item(self,account=None,**kw):
    #~ if account is not None:
    #~ if not isinstance(account,ledger.Account):
    #~ if isinstance(account, str):
    #~ account = self.journal.chart.get_account_by_ref(account)
    #~ kw['account'] = account
    def add_voucher_item(self, account=None, **kw):
        if account is not None:
            if isinstance(account, str):
                account = rt.models.ledger.Account.get_by_ref(account)
            kw['account'] = account
        kw.update(voucher=self)
        #~ logger.info("20131116 %s",self.items.model)
        return self.items.model(**kw)
        #~ return super(AccountInvoice,self).add_voucher_item(**kw)

    def get_bank_account(self):
        """Return the `sepa.Account` object to which this voucher is to be
        paid. This is needed by
        :class:`lino_xl.lib.ledger.utils.DueMovement`.

        """
        return None
        # raise NotImplementedError()

    def get_uploads_volume(self):
        if self.journal_id:
            return self.journal.uploads_volume
Example #56
0
 class Meta:
     app_label = 'finan'
     verbose_name = _("Payment Order item")
     verbose_name_plural = _("Payment Order items")
Example #57
0
 class Meta:
     # abstract = True
     verbose_name = _("Voucher")
     verbose_name_plural = _("Vouchers")
     app_label = 'ledger'
Example #58
0
class Journal(BabelNamed, Sequenced, Referrable, PrintableType):
    class Meta:
        app_label = 'ledger'
        verbose_name = _("Journal")
        verbose_name_plural = _("Journals")

    ref_max_length = 5

    trade_type = TradeTypes.field(blank=True)
    voucher_type = VoucherTypes.field()
    journal_group = JournalGroups.field()
    auto_check_clearings = models.BooleanField(_("Check clearing"),
                                               default=True)
    auto_fill_suggestions = models.BooleanField(_("Fill suggestions"),
                                                default=True)
    force_sequence = models.BooleanField(_("Force chronological sequence"),
                                         default=False)
    preliminary = models.BooleanField(_("Preliminary"), default=False)

    account = dd.ForeignKey('ledger.Account', blank=True, null=True)
    partner = dd.ForeignKey('contacts.Company', 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=True)
    must_declare = models.BooleanField(default=True)
    uploads_volume = dd.ForeignKey("uploads.Volume", blank=True, null=True)

    # invert_due_dc = models.BooleanField(
    #     _("Invert booking direction"),
    #     help_text=_("Whether to invert booking direction of due movement."),
    #     default=True)

    def refuse_missing_partner(self):
        if self.account is None:
            return False
        return True

    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:
            return self.trade_type.get_allowed_accounts(**kw)
        # kw.update(chart=self.chart)
        return rt.models.ledger.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_ui_save(self, ar, cw):
        super(Journal, self).after_ui_save(ar, cw)
        settings.SITE.kernel.must_build_site_cache()

    # 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" % str(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)

    def insert_voucher_button(self, ar):
        table_class = self.get_doc_report()
        sar = table_class.insert_action.request_from(ar, master_instance=self)
        # print(20170217, sar)
        sar.known_values.update(journal=self)
        # sar.known_values.update(journal=self, user=ar.get_user())
        txt = dd.babelattr(self, 'printed_name')
        # txt = self.voucher_type.model._meta.verbose_name_plural
        btn = sar.ar2button(None, _("New {}").format(txt), icon_name=None)
        # btn.set("style", "padding-left:10px")
        return btn
Example #59
0
        # always None.
        if journal:
            fkw = {journal.trade_type.name + '_allowed': True}
            return rt.models.ledger.Account.objects.filter(**fkw)
        print("20151221 journal is None")
        return []


for tt in TradeTypes.objects():
    dd.inject_field(
        'ledger.Account', tt.name + '_allowed',
        models.BooleanField(
            verbose_name=tt.text,
            default=False,
            help_text=format_lazy(
                _("Whether this account is available for {} transactions."),
                tt.text)))

dd.inject_field(
    'contacts.Partner', 'payment_term',
    dd.ForeignKey('ledger.PaymentTerm',
                  blank=True,
                  null=True,
                  help_text=_("The default payment term for "
                              "sales invoices to this customer.")))


class VoucherChecker(Checker):
    verbose_name = _("Check integrity of ledger vouchers")
    messages = dict(
        missing=_("Missing movement {0}."),
Example #60
0
class PaymentOrderDetail(JournalEntryDetail):
    general = dd.Panel("""
    entry_date number:6 total execution_date workflow_buttons
    narration
    finan.ItemsByPaymentOrder
    """, label=_("General"))