diff --git a/rm_extra_data/__init__.py b/rm_extra_data/__init__.py index 0b70eb3..2dcbd82 100644 --- a/rm_extra_data/__init__.py +++ b/rm_extra_data/__init__.py @@ -4,6 +4,9 @@ from . import stock from . import product from . import invoice +from . import routes + +__all__ = ['register', 'routes'] def register(): Pool.register( diff --git a/rm_extra_data/party.py b/rm_extra_data/party.py index 6e9c9c2..62b1d39 100644 --- a/rm_extra_data/party.py +++ b/rm_extra_data/party.py @@ -77,6 +77,42 @@ def copy(cls, parties, default=None): default.setdefault('dolibarr_cid', None) return super().copy(parties, default=default) + def to_vcard(self): + vclines = ['BEGIN:VCARD', 'VERSION:4.0'] + if self.legal_name and self.legal_name.strip(): + # our internal names sometimes are shortened for convenience so use legal_name + vclines.append(f'FN:{self.legal_name.strip()}') + else: + vclines.append(f'FN:{self.name.strip()}') + for ad in self.addresses: + # TODO: do we want to add subdivision ? + street = ad.street.strip().replace("\n", "\\n") # fstring expression part cannot include backslash + vclines.append(f'ADR:;;{street};{ad.city.strip() if ad.city else ""};{ad.postal_code.strip() if ad.postal_code else ""};{ad.subdivision.name.strip() if ad.subdivision and ad.subdivision.name.strip() else ""};{ad.country.name.strip() if ad.country and ad.country.name else ""}') + for cm in self.contact_mechanisms: + # phone number types according to RFC6350: + # https://www.rfc-editor.org/rfc/rfc6350.html#section-6.4.1 + if cm.type == 'phone': + vclines.append(f'TEL;TYPE=voice:{cm.value}') + elif cm.type == 'mobile': + vclines.append(f'TEL;TYPE=cell:{cm.value}') + elif cm.type == 'fax': + vclines.append(f'TEL;TYPE=fax:{cm.value}') + elif cm.type == 'email': + # types WORK and HOME + # https://www.rfc-editor.org/rfc/rfc6350.html#section-6.4.2 + # setting no type since there is no standard way to differenciate between home and work + vclines.append(f'EMAIL:{cm.value}') + elif cm.type == 'website': + pass + elif cm.type in ('skype', 'sip', 'irc', 'jabber'): + vclines.append(f'IMPP;{cm.type}:{cm.value}') + elif cm.type == 'other': + pass + else: + pass + vclines.append('END:VCARD') + return '\n'.join(vclines) + class Replace(metaclass=PoolMeta): __name__ = 'party.replace' diff --git a/rm_extra_data/routes.py b/rm_extra_data/routes.py new file mode 100644 index 0000000..d9e8247 --- /dev/null +++ b/rm_extra_data/routes.py @@ -0,0 +1,18 @@ +from trytond.protocols.wrappers import ( + HTTPStatus, Response, abort, with_pool, with_transaction) +from trytond.pool import Pool +from trytond.wsgi import app + +def create_carddav(): + pool = Pool() + Party = pool.get('party.party') + ret = '' + for p in Party.search([]): + ret += p.to_vcard() + return ret + +@app.route('//party/carddav', methods = {'GET'}) +@with_pool +@with_transaction() +def vcards(request, pool): + return Response(create_carddav(), content_type='text/vcard')