Ejemplo n.º 1
0
def set_widgets(schema):
    """
    Customize form widgets

    :param obj schema: The colander Schema to edit
    """
    customize = functools.partial(forms.customize_field, schema)
    if 'vehicle' in schema:
        customize(
            'vehicle',
            widget=forms.get_deferred_select(
                ExpenseKmType,
                keys=(
                    lambda a: u"%s-%s" % (a.label, a.code),
                    lambda a: u"%s (%s)" % (a.label, a.code)
                ),
                filters=[('active', True)]
            )
        )

    if 'civilite' in schema:
        customize(
            'civilite',
            widget=forms.get_select(CIVILITE_OPTIONS),
            validator=forms.get_select_validator(CIVILITE_OPTIONS)
        )

    if 'email' in schema:
        customize('email', validator=forms.mail_validator())
    return schema
Ejemplo n.º 2
0
def _customize_schema(schema):
    """
    Add common widgets configuration for the customer forms schema

    :param obj schema: The Customer form schema
    """
    schema['civilite'].widget = forms.get_select(CIVILITE_OPTIONS, )
    schema['civilite'].validator = colander.OneOf(
        [a[0] for a in CIVILITE_OPTIONS])
    schema['address'].widget = deform.widget.TextAreaWidget(
        cols=25,
        row=1,
    )
    schema['email'].validator = forms.mail_validator()
    schema['comments'].widget = deform.widget.TextAreaWidget(
        css_class="col-md-8",
        rows=5,
    )
    return schema
Ejemplo n.º 3
0
def _customize_schema(schema):
    """
    Add common widgets configuration for the customer forms schema

    :param obj schema: The Customer form schema
    """
    schema['civilite'].widget = forms.get_select(
        CIVILITE_OPTIONS,
    )
    schema['civilite'].validator = colander.OneOf(
        [a[0] for a in CIVILITE_OPTIONS]
    )
    schema['address'].widget = deform.widget.TextAreaWidget(
        cols=25,
        row=1,
    )
    schema['email'].validator = forms.mail_validator()
    schema['comments'].widget = deform.widget.TextAreaWidget(
        css_class="col-md-8",
        rows=5,
    )
    return schema
Ejemplo n.º 4
0
def set_widgets(schema):
    """
    Customize form widgets

    :param obj schema: The colander Schema to edit
    """
    customize = functools.partial(forms.customize_field, schema)
    if 'vehicle' in schema:
        customize('vehicle',
                  widget=forms.get_deferred_select(ExpenseKmType,
                                                   keys=(lambda a: u"%s-%s" %
                                                         (a.label, a.code),
                                                         lambda a: u"%s (%s)" %
                                                         (a.label, a.code)),
                                                   filters=[('active', True)]))

    if 'civilite' in schema:
        customize('civilite',
                  widget=forms.get_select(CIVILITE_OPTIONS),
                  validator=forms.get_select_validator(CIVILITE_OPTIONS))

    if 'email' in schema:
        customize('email', validator=forms.mail_validator())
    return schema
Ejemplo n.º 5
0
class Customer(DBBASE, PersistentACLMixin):
    """
        Customer model
        Stores the company and its main contact
        :param name: name of the company
        :param code: internal code of the customer (unique regarding the owner)
        :param multiline address: address of the company
        :param zip_code: zipcode of the company
        :param city: city
        :param country: country, default France
        :param lastname: lastname of the contact
        :param firstname: firstname of the contact
        :param function: function of the contact
    """
    __tablename__ = 'customer'
    __table_args__ = default_table_args
    __colanderalchemy_config__ = {
        'after_bind': customer_after_bind,
    }
    id = Column(
        'id',
        Integer,
        primary_key=True,
        info={
            'colanderalchemy': {
                'exclude': True,
                'title': u"Identifiant Autonomie",
            }
        },
    )
    type_ = Column('type_',
                   String(10),
                   default='company',
                   info={
                       'colanderalchemy': {
                           'exclude': True
                       },
                       'export': {
                           'exclude': True
                       },
                   })

    created_at = deferred(
        Column(
            Date(),
            default=datetime.date.today,
            info={
                'export': {
                    'exclude': True
                },
                'colanderalchemy': forms.EXCLUDED,
            },
            nullable=False,
        ),
        group='all',
    )

    updated_at = deferred(
        Column(
            Date(),
            default=datetime.date.today,
            onupdate=datetime.date.today,
            info={
                'export': {
                    'exclude': True
                },
                'colanderalchemy': forms.EXCLUDED,
            },
            nullable=False,
        ),
        group='all',
    )

    company_id = Column(
        "company_id",
        Integer,
        ForeignKey('company.id'),
        info={
            'export': {
                'exclude': True
            },
            'colanderalchemy': forms.EXCLUDED,
        },
        nullable=False,
    )

    name = Column(
        "name",
        String(255),
        info={
            "colanderalchemy": {
                'title': u'Nom de la structure',
            },
        },
        default='',
    )

    code = Column(
        'code',
        String(4),
        info={'colanderalchemy': {
            'title': u"Code client"
        }},
    )

    civilite = deferred(
        Column('civilite',
               String(10),
               info={
                   'colanderalchemy': {
                       'title':
                       u"Civilité",
                       'widget':
                       forms.get_radio(CIVILITE_OPTIONS[1:], inline=True),
                   }
               }),
        group='edit',
    )

    lastname = deferred(
        Column(
            "lastname",
            String(255),
            info={"colanderalchemy": {
                'title': u"Nom du contact principal",
            }},
            nullable=False,
        ),
        group='edit',
    )

    firstname = deferred(
        Column(
            "firstname",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u"Prénom du contact principal",
                }
            },
            default="",
        ),
        group='edit',
    )

    function = deferred(
        Column(
            "function",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u"Fonction du contact principal",
                }
            },
            default='',
        ),
        group="edit",
    )

    address = deferred(Column(
        "address",
        String(255),
        info={
            'colanderalchemy': {
                'title': u'Adresse',
                'widget': deform.widget.TextAreaWidget(
                    cols=25,
                    row=1,
                )
            }
        },
        nullable=False,
    ),
                       group='edit')

    zip_code = deferred(
        Column(
            "zip_code",
            String(20),
            info={
                'colanderalchemy': {
                    'title': u'Code postal',
                },
            },
            nullable=False,
        ),
        group='edit',
    )

    city = deferred(
        Column(
            "city",
            String(255),
            info={'colanderalchemy': {
                'title': u'Ville',
            }},
            nullable=False,
        ),
        group='edit',
    )

    country = deferred(
        Column(
            "country",
            String(150),
            info={
                'colanderalchemy': {
                    'title': u'Pays'
                },
            },
            default=u'France',
        ),
        group='edit',
    )

    email = deferred(
        Column(
            "email",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u"Adresse de messagerie",
                    'validator': forms.mail_validator(),
                },
            },
            default='',
        ),
        group='edit',
    )
    mobile = deferred(
        Column(
            "mobile",
            String(20),
            info={
                'colanderalchemy': {
                    'title': u"Téléphone portable",
                },
            },
            default='',
        ),
        group='edit',
    )

    phone = deferred(
        Column(
            "phone",
            String(50),
            info={
                'colanderalchemy': {
                    'title': u'Téléphone fixe',
                },
            },
            default='',
        ),
        group='edit',
    )

    fax = deferred(Column(
        "fax",
        String(50),
        info={'colanderalchemy': {
            'title': u'Fax',
        }},
        default='',
    ),
                   group="edit")

    tva_intracomm = deferred(
        Column(
            "tva_intracomm",
            String(50),
            info={
                'colanderalchemy': {
                    'title': u"TVA intracommunautaire"
                },
            },
            default='',
        ),
        group='edit',
    )

    comments = deferred(
        Column(
            "comments",
            Text,
            info={
                'colanderalchemy': {
                    'title': u"Commentaires",
                    'widget':
                    deform.widget.TextAreaWidget(css_class="col-md-10"),
                }
            },
        ),
        group='edit',
    )

    compte_cg = deferred(
        Column(
            String(125),
            info={
                'export': {
                    'exclude': True
                },
                'colanderalchemy': {
                    'title': u"Compte CG",
                },
            },
            default="",
        ),
        group="edit",
    )

    compte_tiers = deferred(
        Column(
            String(125),
            info={
                'export': {
                    'exclude': True
                },
                'colanderalchemy': {
                    'title': u"Compte tiers",
                }
            },
            default="",
        ),
        group="edit",
    )
    archived = Column(
        Boolean(),
        default=False,
        info={'colanderalchemy': forms.EXCLUDED},
    )

    company = relationship("Company",
                           primaryjoin="Company.id==Customer.company_id",
                           info={
                               'colanderalchemy': forms.EXCLUDED,
                               'export': {
                                   'exclude': True
                               },
                           })

    estimations = relationship(
        "Estimation",
        primaryjoin="Estimation.customer_id==Customer.id",
        info={
            'colanderalchemy': forms.EXCLUDED,
            'export': {
                'exclude': True
            },
        })

    invoices = relationship("Invoice",
                            primaryjoin="Invoice.customer_id==Customer.id",
                            info={
                                'colanderalchemy': forms.EXCLUDED,
                                'export': {
                                    'exclude': True
                                },
                            })

    cancelinvoices = relationship(
        "CancelInvoice",
        primaryjoin="CancelInvoice.customer_id==Customer.id",
        info={
            'colanderalchemy': forms.EXCLUDED,
            'export': {
                'exclude': True
            },
        })

    _autonomie_service = CustomerService

    def get_company_id(self):
        """
            :returns: the id of the company this customer belongs to
        """
        return self.company.id

    def todict(self):
        """
            :returns: a dict version of the customer object
        """
        projects = [project.todict() for project in self.projects]
        return dict(
            id=self.id,
            code=self.code,
            comments=self.comments,
            tva_intracomm=self.tva_intracomm,
            address=self.address,
            zip_code=self.zip_code,
            city=self.city,
            country=self.country,
            phone=self.phone,
            email=self.email,
            lastname=self.lastname,
            firstname=self.firstname,
            name=self.name,
            projects=projects,
            full_address=self.full_address,
            archived=self.archived,
            company_id=self.company_id,
        )

    def __json__(self, request):
        return self.todict()

    @property
    def full_address(self):
        """
            :returns: the customer address formatted in french format
        """
        return self._autonomie_service.get_address(self)

    def has_tasks(self):
        return self._autonomie_service.count_tasks(self) > 0

    def is_deletable(self):
        """
            Return True if this project could be deleted
        """
        return self.archived and not self.has_tasks()

    def is_company(self):
        return self.type_ == 'company'

    def get_label(self):
        return self._autonomie_service.get_label(self)

    @property
    def label(self):
        """
        Property used for exports (as a related_key parameter)
        """
        return self.get_label()

    def get_name(self):
        return self._autonomie_service.format_name(self)

    @classmethod
    def check_project_id(cls, customer_id, project_id):
        return cls._autonomie_service.check_project_id(customer_id, project_id)
Ejemplo n.º 6
0
def customize_schema(schema):
    """
    Customize the form schema
    :param obj schema: A UserDatas schema
    """
    customize = functools.partial(customize_field, schema)

    customize(
        'situation_antenne_id',
        get_deferred_select(AntenneOption)
    )

    customize(
        'situation_follower_id',
        get_deferred_user_choice(
            roles=['admin', 'manager'],
            widget_options={
                'default_option': ('', ''),
            }
        )
    )

    customize(
        'coordonnees_civilite',
        get_select(CIVILITE_OPTIONS)
    )

    customize('coordonnees_email1', validator=mail_validator())
    customize('coordonnees_email2', validator=mail_validator())

    customize(
        'coordonnees_address',
        deform.widget.TextAreaWidget(),
    )

    customize(
        "coordonnees_zone_id",
        get_deferred_select(ZoneOption),
    )

    customize(
        "coordonnees_zone_qual_id",
        get_deferred_select(ZoneQualificationOption),
    )

    customize(
        "coordonnees_sex",
        get_select(SEX_OPTIONS),
        get_select_validator(SEX_OPTIONS)
    )

    customize(
        "coordonnees_birthplace",
        deform.widget.TextAreaWidget(),
    )

    customize(
        "coordonnees_family_status",
        get_select(STATUS_OPTIONS),
        get_select_validator(STATUS_OPTIONS),
    )

    customize(
        "coordonnees_children",
        get_select(zip(range(20), range(20)))
    )

    customize(
        "coordonnees_study_level_id",
        get_deferred_select(StudyLevelOption),
    )

    customize(
        "statut_social_status_id",
        get_deferred_select(SocialStatusOption),
    )

    customize(
        "statut_social_status_today_id",
        get_deferred_select(SocialStatusOption),
    )

    customize(
        "activity_typologie_id",
        get_deferred_select(ActivityTypeOption)
    )

    customize(
        "activity_pcs_id",
        get_deferred_select(PcsOption)
    )

    customize(
        "parcours_prescripteur_id",
        get_deferred_select(PrescripteurOption),
    )

    customize(
        "parcours_non_admission_id",
        get_deferred_select(NonAdmissionOption),
    )

    if 'social_statuses' in schema:
        child_schema = schema['social_statuses'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'social_status_id',
            widget=get_deferred_select(SocialStatusOption)
        )
        customize_field(
            child_schema,
            'step',
            widget=deform.widget.HiddenWidget(),
            default="entry"
        )

    if 'today_social_statuses' in schema:
        child_schema = schema['today_social_statuses'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'social_status_id',
            widget=get_deferred_select(SocialStatusOption)
        )
        customize_field(
            child_schema,
            'step',
            widget=deform.widget.HiddenWidget(),
            default="today"
        )

    if 'statut_external_activity' in schema:
        child_schema = schema['statut_external_activity'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'statut_external_activity',
            widget=get_select(CONTRACT_OPTIONS),
        )

    if 'activity_companydatas' in schema:
        child_schema = schema['activity_companydatas'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'activity_id',
            widget=get_deferred_select(CompanyActivity)
        )

    customize("parcours_goals", deform.widget.TextAreaWidget())
    customize('parcours_status_id', get_deferred_select(ParcoursStatusOption))
Ejemplo n.º 7
0
def customize_schema(schema):
    """
    Customize the form schema
    :param obj schema: A UserDatas schema
    """
    customize = functools.partial(customize_field, schema)

    customize(
        'situation_antenne_id',
        get_deferred_select(AntenneOption)
    )

    customize(
        'situation_follower_id',
        get_deferred_user_choice(
            roles=['admin', 'manager'],
            widget_options={
                'default_option': ('', ''),
            }
        )
    )

    customize(
        'coordonnees_civilite',
        get_select(CIVILITE_OPTIONS)
    )

    customize('coordonnees_email1', validator=mail_validator())
    customize('coordonnees_email2', validator=mail_validator())

    customize(
        'coordonnees_address',
        deform.widget.TextAreaWidget(),
    )

    customize(
        "coordonnees_zone_id",
        get_deferred_select(ZoneOption),
    )

    customize(
        "coordonnees_zone_qual_id",
        get_deferred_select(ZoneQualificationOption),
    )

    customize(
        "coordonnees_sex",
        get_select(SEX_OPTIONS),
        get_select_validator(SEX_OPTIONS)
    )

    customize(
        "coordonnees_birthplace",
        deform.widget.TextAreaWidget(),
    )

    customize(
        "coordonnees_family_status",
        get_select(STATUS_OPTIONS),
        get_select_validator(STATUS_OPTIONS),
    )

    customize(
        "coordonnees_children",
        get_select(zip(range(20), range(20)))
    )

    customize(
        "coordonnees_study_level_id",
        get_deferred_select(StudyLevelOption),
    )

    customize(
        "statut_social_status_id",
        get_deferred_select(SocialStatusOption),
    )

    customize(
        "statut_social_status_today_id",
        get_deferred_select(SocialStatusOption),
    )

    customize(
        "activity_typologie_id",
        get_deferred_select(ActivityTypeOption)
    )

    customize(
        "activity_pcs_id",
        get_deferred_select(PcsOption)
    )

    customize(
        "parcours_prescripteur_id",
        get_deferred_select(PrescripteurOption),
    )

    customize(
        "parcours_non_admission_id",
        get_deferred_select(NonAdmissionOption),
    )

    if 'social_statuses' in schema:
        child_schema = schema['social_statuses'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'social_status_id',
            widget=get_deferred_select(SocialStatusOption)
        )
        customize_field(
            child_schema,
            'step',
            widget=deform.widget.HiddenWidget(),
            default="entry"
        )

    if 'today_social_statuses' in schema:
        child_schema = schema['today_social_statuses'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'social_status_id',
            widget=get_deferred_select(SocialStatusOption)
        )
        customize_field(
            child_schema,
            'step',
            widget=deform.widget.HiddenWidget(),
            default="today"
        )

    if 'statut_external_activity' in schema:
        child_schema = schema['statut_external_activity'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'statut_external_activity',
            widget=get_select(CONTRACT_OPTIONS),
        )

    if 'activity_companydatas' in schema:
        child_schema = schema['activity_companydatas'].children[0]
        child_schema.widget = CleanMappingWidget()
        customize_field(
            child_schema,
            'activity_id',
            widget=get_deferred_select(CompanyActivity)
        )

    customize("parcours_goals", deform.widget.TextAreaWidget())
    customize('parcours_status_id', get_deferred_select(ParcoursStatusOption))
Ejemplo n.º 8
0
class Customer(DBBASE, PersistentACLMixin):
    """
        Customer model
        Stores the company and its main contact
        :param name: name of the company
        :param code: internal code of the customer (unique regarding the owner)
        :param multiline address: address of the company
        :param zipCode: zipcode of the company
        :param city: city
        :param country: country, default France
        :param contactLastName: lastname of the contact
        :param contactFirstName: firstname of the contact
        :param function: function of the contact
    """
    __tablename__ = 'customer'
    __table_args__ = default_table_args
    id = Column(
        'id',
        Integer,
        primary_key=True,
        info={
            'colanderalchemy': {
                'exclude': True,
                'title': u"Identifiant Autonomie",
            }
        },
    )

    created_at = deferred(
        Column(
            "creationDate",
            CustomDateType,
            default=get_current_timestamp,
            info={
                'export':{'exclude':True},
                'colanderalchemy': forms.EXCLUDED,
            },
        ),
        group='all',
    )

    updated_at = deferred(
        Column(
            "updateDate",
            CustomDateType,
            default=get_current_timestamp,
            onupdate=get_current_timestamp,
            info={
                'export':{'exclude':True},
                'colanderalchemy': forms.EXCLUDED,
            },
        ),
        group='all',
    )

    company_id = Column(
        "company_id",
        Integer,
        ForeignKey('company.id'),
        info={
            'export':{'exclude':True},
            'colanderalchemy': forms.EXCLUDED,
        }
    )

    name = Column(
        "name",
        String(255),
        info={
            "colanderalchemy": {
                'title': u'Nom',
            },
        },
        nullable=False,
    )

    code = Column(
        'code',
        String(4),
        info={
            'colanderalchemy':{
                'title': u"Code",
                'widget': deform.widget.TextInputWidget(mask='****'),
                'validator': deferred_ccode_valid,
            }
        },
        nullable=False,
    )

    contactLastName = deferred(
        Column(
            "contactLastName",
            String(255),
            info={
                "colanderalchemy": {
                    'title':u"Nom du contact principal",
                }
            },
            nullable=False,
        ),
        group='edit',
    )

    contactFirstName = deferred(
        Column(
            "contactFirstName",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u"Prénom du contact principal",
                }
            },
            default="",
        ),
        group='edit',
    )

    function = deferred(
        Column(
            "function",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u"Fonction du contact principal",
                }
            },
            default='',
        ),
        group="edit",
    )

    address = deferred(
        Column(
            "address",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u'Adresse',
                    'widget': deform.widget.TextAreaWidget(
                        cols=25,
                        row=1,
                    )
                }
            },
            nullable=False,
        ),
        group='edit'
    )

    zipCode = deferred(
        Column(
            "zipCode",
            String(20),
            info={
                'colanderalchemy':{
                    'title': u'Code postal',
                },
            },
            nullable=False,
        ),
        group='edit',
    )

    city = deferred(
        Column(
            "city",
            String(255),
            info={
                'colanderalchemy': {
                    'title': u'Ville',
                }
            },
            nullable=False,
        ),
        group='edit',
    )

    country = deferred(
        Column(
            "country",
            String(150),
            info={
                'colanderalchemy': {'title': u'Pays'},
            },
            default=u'France',
        ),
        group='edit',
    )

    email = deferred(
        Column(
            "email",
            String(255),
            info={
                'colanderalchemy':{
                    'title': u"E-mail",
                    'validator': forms.mail_validator(),
                },
            },
            default='',
        ),
        group='edit',
    )

    phone = deferred(
        Column(
            "phone",
            String(50),
            info={
                'colanderalchemy': {
                    'title': u'Téléphone',
                },
            },
            default='',
        ),
        group='edit',
    )

    fax = deferred(
        Column(
            "fax",
            String(50),
            info={
                'colanderalchemy': {
                    'title': u'Fax',
                }
            },
            default='',
        ),
        group="edit"
    )

    intraTVA = deferred(
        Column(
            "intraTVA",
            String(50),
            info={
                'colanderalchemy': {'title': u"TVA intracommunautaire"},
            },
        ),
        group='edit',
    )

    comments = deferred(
        Column(
            "comments",
            Text,
            info={
                  'colanderalchemy':{
                      'title': u"Commentaires",
                      'widget': deform.widget.TextAreaWidget(
                          css_class="col-md-10"
                      ),
                  }
            },
        ),
        group='edit',
    )

    compte_cg = deferred(
        Column(
            String(125),
            info={
                'export': {'exclude': True},
                'colanderalchemy': {
                    'title': u"Compte CG",
                },
            },
            default="",
        ),
        group="edit",
    )

    compte_tiers = deferred(
        Column(
            String(125),
            info={
                'export': {'exclude': True},
                'colanderalchemy': {
                    'title': u"Compte tiers",
                }
            },
            default="",
        ),
        group="edit",
    )
    archived = Column(
        Boolean(),
        default=False,
        info={'colanderalchemy': forms.EXCLUDED},
    )


    def get_company_id(self):
        """
            :returns: the id of the company this customer belongs to
        """
        return self.company.id

    def todict(self):
        """
            :returns: a dict version of the customer object
        """
        projects = [project.todict() for project in self.projects]
        return dict(
            id=self.id,
            code=self.code,
            comments=self.comments,
            intraTVA=self.intraTVA,
            address=self.address,
            zipCode=self.zipCode,
            city=self.city,
            country=self.country,
            phone=self.phone,
            email=self.email,
            contactLastName=self.contactLastName,
            contactFirstName=self.contactFirstName,
            name=self.name,
            projects=projects,
            full_address=self.full_address,
            archived=self.archived,
        )

    def __json__(self, request):
        return self.todict()

    @property
    def full_address(self):
        """
            :returns: the customer address formatted in french format
        """
        address = u"{name}\n{address}\n{zipCode} {city}".format(name=self.name,
                address=self.address, zipCode=self.zipCode, city=self.city)
        if self.country not in ("France", "france"):
            address += u"\n{0}".format(self.country)
        return address

    def get_associated_tasks_by_type(self, type_str):
        """
        Return the tasks of type type_str associated to the current object
        """
        from autonomie.models.task import Task
        return DBSESSION().query(Task).filter_by(
            customer_id=self.id, type_=type_str
        ).all()

    @property
    def invoices(self):
        return self.get_associated_tasks_by_type('invoice')

    @property
    def estimations(self):
        return self.get_associated_tasks_by_type('estimation')

    @property
    def cancelinvoices(self):
        return self.get_associated_tasks_by_type('cancelinvoice')

    def has_tasks(self):
        from autonomie.models.task import Task
        num = DBSESSION().query(Task.id).filter_by(customer_id=self.id).count()
        return num > 0

    def is_deletable(self):
        """
            Return True if this project could be deleted
        """
        return self.archived and not self.has_tasks()