39 Commits
0.0.2 ... 0.0.3

Author SHA1 Message Date
df4adecda1 fix 2021-09-10 19:07:08 +02:00
412e2b61f3 date fixes 2021-09-10 13:35:12 +02:00
2a23d88dd9 fixes 2021-09-10 12:40:48 +02:00
2da6b667bc fixes 2021-09-10 11:59:08 +02:00
554a809ba4 fix 2021-09-09 18:43:27 +02:00
204d2a27f2 notes 2021-09-09 18:39:18 +02:00
6fcd785be0 locale stuff 2021-09-09 18:00:24 +02:00
8276e39a7e account entry stuff again 2021-09-09 17:01:27 +02:00
e55578759f account entries stuff 2021-09-09 16:39:05 +02:00
eae137bea8 fix 2021-09-09 15:52:38 +02:00
75120b1d80 account at tenant 2021-09-09 15:49:49 +02:00
b394a16d7e some changes about fee and tenancy mapping 2021-09-09 11:36:01 +02:00
3dfb7e2bf2 accordion 2021-09-08 18:47:05 +02:00
0ae59c644e changes 2021-09-08 18:13:59 +02:00
c6e865eca1 overhead advance mapping 2021-09-08 14:33:01 +02:00
6b8b332758 code beautify 2021-09-08 14:32:43 +02:00
acc2170455 Betriebskostenzuordnung 2021-09-08 13:20:00 +02:00
76cbcc8afc add special method here too 2021-09-08 13:11:42 +02:00
7de0ab2db9 add special method 2021-09-08 13:11:28 +02:00
01bd6877c6 add foreign key service methods 2021-09-08 12:39:23 +02:00
bb2aaa1e84 generate endpoint for all foreign key relationships 2021-09-08 12:33:26 +02:00
1335f0f059 additional endpoint mechanism and overhead advance mapping by flat method 2021-09-08 12:18:09 +02:00
cf35af9c1e fee pages 2021-09-07 17:52:02 +02:00
356c8c0bbd re-enable submit button in finally block 2021-09-07 16:02:09 +02:00
d21a5986a8 drop mislocated put definition from yaml 2021-09-07 15:46:31 +02:00
6470aa5358 update methods and immutable attribute added 2021-09-07 15:22:55 +02:00
0afef9f3cd unsubscribe an existing subscription before creating a new one (avoids jumps in session timer in case of multiple query on one page) 2021-09-07 14:44:48 +02:00
fd25f1fcf5 expirytime finally working 2021-09-06 18:12:45 +02:00
9478ad27c6 Merge branch 'master' of https://home.hottis.de/gitlab/hv2/hv2-all-in-one 2021-09-06 14:04:17 +02:00
bbe698fc12 expiry display not working 2021-09-06 14:03:08 +02:00
606ac6d81f THERE IS A RACE CONDITION BETWEEN USE AND REPEATED REFRESH OF A TOKEN 2021-09-03 22:07:52 +02:00
657d85538e expiry display, not yet working 2021-09-03 20:13:45 +02:00
2312f21d77 refresh token support 2021-09-03 19:31:46 +02:00
65d10685a4 Merge branch 'master' of https://home.hottis.de/gitlab/hv2/hv2-all-in-one 2021-09-03 11:57:52 +02:00
5957662f2d debug in authentication path 2021-09-03 11:57:46 +02:00
974415a93a fix insert of empty strings 2021-09-02 21:57:06 +02:00
1ce54cf9cf overhead stuff 2021-09-02 18:26:55 +02:00
7c180f0986 more details pages 2021-09-02 15:50:29 +02:00
ce4ff75bca add NULL objects to template 2021-09-02 15:37:43 +02:00
80 changed files with 4349 additions and 251 deletions

View File

@ -0,0 +1,75 @@
# -------------------------------------------------------------------
# ATTENTION: This file will not be parsed by Cheetah
# Use plain openapi/yaml syntax, no Cheetah
# escaping
# -------------------------------------------------------------------
/v1/overhead_advances/flat/{flatId}:
get:
tags: [ "overhead_advance", "flat" ]
summary: Return overhead_advances by flat
operationId: additional_methods.get_overhead_advances_by_flat
parameters:
- name: flatId
in: path
required: true
schema:
type: integer
responses:
'200':
description: overhead_advances_by_flat response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance'
security:
- jwt: ['secret']
/v1/fees/tenancy/{tenancyId}:
get:
tags: [ "fee", "tenancy" ]
summary: Return fees by tenancy
operationId: additional_methods.get_fees_by_tenancy
parameters:
- name: tenancyId
in: path
required: true
schema:
type: integer
responses:
'200':
description: get_fees_by_tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/fee'
security:
- jwt: ['secret']
/v1/account/saldo/{accountId}:
get:
tags: [ "account" ]
summary: Return saldo of the account
operationId: additional_methods.get_account_saldo
parameters:
- name: accountId
in: path
required: true
schema:
type: integer
responses:
'200':
description: get_account_saldo
content:
'application/json':
schema:
type: object
properties:
saldo:
type: number
security:
- jwt: ['secret']

34
api/additional_methods.py Normal file
View File

@ -0,0 +1,34 @@
from db import dbGetMany, dbGetOne, dbInsert, dbUpdate
from loguru import logger
import werkzeug
def get_overhead_advances_by_flat(user, token_info, flatId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT o.id ,o.description ,o.amount ,o.startdate ,o.enddate
FROM overhead_advance_t o, overhead_advance_flat_mapping_t m
WHERE o.id = m.overhead_advance and m.flat = %s""",
"params": (flatId, )
}
)
def get_fees_by_tenancy(user, token_info, tenancyId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT o.id, o.description, o.amount, o.fee_type, o.startdate, o.enddate
FROM fee_t o, tenancy_fee_mapping_t m
WHERE o.id = m.fee and m.tenancy = %s""",
"params": (tenancyId, )
}
)
def get_account_saldo(user, token_info, accountId=None):
return dbGetOne(user, token_info, {
"statement": "SELECT sum(amount) as saldo FROM account_entry_t WHERE account=%s",
"params": (accountId, )
}
)

View File

@ -51,6 +51,8 @@ def execDatabaseOperation(func, params):
with conn: with conn:
with conn.cursor(cursor_factory = psycopg2.extras.RealDictCursor) as cur: with conn.cursor(cursor_factory = psycopg2.extras.RealDictCursor) as cur:
params["params"] = [ v if not v=='' else None for v in params["params"] ]
logger.debug("edo: {}".format(str(params)))
return func(cur, params) return func(cur, params)
except psycopg2.Error as err: except psycopg2.Error as err:
raise Exception("Error when connecting to database: {}".format(err)) raise Exception("Error when connecting to database: {}".format(err))
@ -62,7 +64,7 @@ def execDatabaseOperation(func, params):
def _opGetMany(cursor, params): def _opGetMany(cursor, params):
items = [] items = []
cursor.execute(params["statement"]) cursor.execute(params["statement"], params["params"])
for itemObj in cursor: for itemObj in cursor:
logger.debug("item received {}".format(str(itemObj))) logger.debug("item received {}".format(str(itemObj)))
items.append(itemObj) items.append(itemObj)
@ -110,3 +112,11 @@ def dbInsert(user, token_info, params):
except Exception as e: except Exception as e:
logger.error(f"Exception: {e}") logger.error(f"Exception: {e}")
raise werkzeug.exceptions.InternalServerError raise werkzeug.exceptions.InternalServerError
def dbUpdate(user, token_info, params):
logger.info("params: {}, token: {}".format(params, json.dumps(token_info)))
try:
return execDatabaseOperation(_opGetOne, params)
except Exception as e:
logger.error(f"Exception: {e}")
raise werkzeug.exceptions.InternalServerError

View File

@ -5,8 +5,9 @@
# ----------------------------------------- # -----------------------------------------
from db import dbGetMany, dbGetOne, dbInsert from db import dbGetMany, dbGetOne, dbInsert, dbUpdate
from loguru import logger from loguru import logger
import werkzeug
def get_accounts(user, token_info): def get_accounts(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
@ -57,6 +58,29 @@ SELECT
"params": (accountId, ) "params": (accountId, )
} }
) )
def update_account(user, token_info, accountId=None, **args):
try:
body = args["body"]
v_description = body["description"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE account_t
SET
description = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
accountId
]
})
except KeyError as e:
logger.warning("update_account: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenants(user, token_info): def get_tenants(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -173,6 +197,83 @@ SELECT
"params": (tenantId, ) "params": (tenantId, )
} }
) )
def update_tenant(user, token_info, tenantId=None, **args):
try:
body = args["body"]
v_salutation = body["salutation"]
v_firstname = body["firstname"]
v_lastname = body["lastname"]
v_address1 = body["address1"]
v_address2 = body["address2"]
v_address3 = body["address3"]
v_zip = body["zip"]
v_city = body["city"]
v_phone1 = body["phone1"]
v_phone2 = body["phone2"]
v_iban = body["iban"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE tenant_t
SET
salutation = %s
,firstname = %s
,lastname = %s
,address1 = %s
,address2 = %s
,address3 = %s
,zip = %s
,city = %s
,phone1 = %s
,phone2 = %s
,iban = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_salutation,
v_firstname,
v_lastname,
v_address1,
v_address2,
v_address3,
v_zip,
v_city,
v_phone1,
v_phone2,
v_iban,
tenantId
]
})
except KeyError as e:
logger.warning("update_tenant: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenant_by_account(user, token_info, accountId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,salutation
,firstname
,lastname
,address1
,address2
,address3
,zip
,city
,phone1
,phone2
,iban
,account
FROM tenant_t
WHERE account = %s
""",
"params": (accountId, )
}
)
def get_premises(user, token_info): def get_premises(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -240,6 +341,38 @@ SELECT
"params": (premiseId, ) "params": (premiseId, )
} }
) )
def update_premise(user, token_info, premiseId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_street = body["street"]
v_zip = body["zip"]
v_city = body["city"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE premise_t
SET
description = %s
,street = %s
,zip = %s
,city = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_street,
v_zip,
v_city,
premiseId
]
})
except KeyError as e:
logger.warning("update_premise: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_flats(user, token_info): def get_flats(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -308,6 +441,54 @@ SELECT
"params": (flatId, ) "params": (flatId, )
} }
) )
def update_flat(user, token_info, flatId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
v_area = body["area"]
v_flat_no = body["flat_no"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE flat_t
SET
description = %s
,premise = %s
,area = %s
,flat_no = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_premise,
v_area,
v_flat_no,
flatId
]
})
except KeyError as e:
logger.warning("update_flat: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_flat_by_premise(user, token_info, premiseId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,premise
,area
,flat_no
FROM flat_t
WHERE premise = %s
""",
"params": (premiseId, )
}
)
def get_overhead_advances(user, token_info): def get_overhead_advances(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -376,6 +557,32 @@ SELECT
"params": (overhead_advanceId, ) "params": (overhead_advanceId, )
} }
) )
def update_overhead_advance(user, token_info, overhead_advanceId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_enddate = body["enddate"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE overhead_advance_t
SET
description = %s
,enddate = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_enddate,
overhead_advanceId
]
})
except KeyError as e:
logger.warning("update_overhead_advance: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_overhead_advance_flat_mappings(user, token_info): def get_overhead_advance_flat_mappings(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -432,6 +639,37 @@ SELECT
"params": (overhead_advance_flat_mappingId, ) "params": (overhead_advance_flat_mappingId, )
} }
) )
def get_overhead_advance_flat_mapping_by_overhead_advance(user, token_info, overhead_advanceId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,overhead_advance
,flat
FROM overhead_advance_flat_mapping_t
WHERE overhead_advance = %s
""",
"params": (overhead_advanceId, )
}
)
def get_overhead_advance_flat_mapping_by_flat(user, token_info, flatId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,overhead_advance
,flat
FROM overhead_advance_flat_mapping_t
WHERE flat = %s
""",
"params": (flatId, )
}
)
def get_parkings(user, token_info): def get_parkings(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -488,6 +726,46 @@ SELECT
"params": (parkingId, ) "params": (parkingId, )
} }
) )
def update_parking(user, token_info, parkingId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE parking_t
SET
description = %s
,premise = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_premise,
parkingId
]
})
except KeyError as e:
logger.warning("update_parking: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_parking_by_premise(user, token_info, premiseId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,premise
FROM parking_t
WHERE premise = %s
""",
"params": (premiseId, )
}
)
def get_commercial_premises(user, token_info): def get_commercial_premises(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -544,6 +822,46 @@ SELECT
"params": (commercial_premiseId, ) "params": (commercial_premiseId, )
} }
) )
def update_commercial_premise(user, token_info, commercial_premiseId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE commercial_premise_t
SET
description = %s
,premise = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_premise,
commercial_premiseId
]
})
except KeyError as e:
logger.warning("update_commercial_premise: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_commercial_premise_by_premise(user, token_info, premiseId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,premise
FROM commercial_premise_t
WHERE premise = %s
""",
"params": (premiseId, )
}
)
def get_tenancys(user, token_info): def get_tenancys(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -630,6 +948,108 @@ SELECT
"params": (tenancyId, ) "params": (tenancyId, )
} }
) )
def update_tenancy(user, token_info, tenancyId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_enddate = body["enddate"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE tenancy_t
SET
description = %s
,enddate = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_enddate,
tenancyId
]
})
except KeyError as e:
logger.warning("update_tenancy: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenancy_by_tenant(user, token_info, tenantId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,tenant
,flat
,parking
,commercial_premise
,startdate
,enddate
FROM tenancy_t
WHERE tenant = %s
""",
"params": (tenantId, )
}
)
def get_tenancy_by_flat(user, token_info, flatId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,tenant
,flat
,parking
,commercial_premise
,startdate
,enddate
FROM tenancy_t
WHERE flat = %s
""",
"params": (flatId, )
}
)
def get_tenancy_by_parking(user, token_info, parkingId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,tenant
,flat
,parking
,commercial_premise
,startdate
,enddate
FROM tenancy_t
WHERE parking = %s
""",
"params": (parkingId, )
}
)
def get_tenancy_by_commercial_premise(user, token_info, commercial_premiseId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,tenant
,flat
,parking
,commercial_premise
,startdate
,enddate
FROM tenancy_t
WHERE commercial_premise = %s
""",
"params": (commercial_premiseId, )
}
)
def get_fees(user, token_info): def get_fees(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -704,6 +1124,32 @@ SELECT
"params": (feeId, ) "params": (feeId, )
} }
) )
def update_fee(user, token_info, feeId=None, **args):
try:
body = args["body"]
v_description = body["description"]
v_enddate = body["enddate"]
return dbUpdate(user, token_info, {
"statement": """
UPDATE fee_t
SET
description = %s
,enddate = %s
WHERE id = %s
RETURNING *
""",
"params": [
v_description,
v_enddate,
feeId
]
})
except KeyError as e:
logger.warning("update_fee: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenancy_fee_mappings(user, token_info): def get_tenancy_fee_mappings(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -757,6 +1203,95 @@ SELECT
"params": (tenancy_fee_mappingId, ) "params": (tenancy_fee_mappingId, )
} }
) )
def get_tenancy_fee_mapping_by_tenancy(user, token_info, tenancyId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,tenancy
,fee
FROM tenancy_fee_mapping_t
WHERE tenancy = %s
""",
"params": (tenancyId, )
}
)
def get_tenancy_fee_mapping_by_fee(user, token_info, feeId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,tenancy
,fee
FROM tenancy_fee_mapping_t
WHERE fee = %s
""",
"params": (feeId, )
}
)
def get_account_entry_categorys(user, token_info):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,overhead_relevant
FROM account_entry_category_t
ORDER BY
description
""",
"params": ()
}
)
def insert_account_entry_category(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_overhead_relevant = body["overhead_relevant"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO account_entry_category_t
(
description
,overhead_relevant
) VALUES (
%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_overhead_relevant
]
})
except KeyError as e:
logger.warning("insert_account_entry_category: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_account_entry_category(user, token_info, account_entry_categoryId=None):
return dbGetOne(user, token_info, {
"statement": """
SELECT
id
,description
,overhead_relevant
FROM account_entry_category_t
WHERE id = %s
""",
"params": (account_entry_categoryId, )
}
)
def get_account_entrys(user, token_info): def get_account_entrys(user, token_info):
return dbGetMany(user, token_info, { return dbGetMany(user, token_info, {
"statement": """ "statement": """
@ -766,7 +1301,10 @@ SELECT
,account ,account
,created_at ,created_at
,amount ,amount
,account_entry_category
FROM account_entry_t FROM account_entry_t
ORDER BY
amount
""", """,
"params": () "params": ()
} }
@ -779,6 +1317,7 @@ def insert_account_entry(user, token_info, **args):
v_account = body["account"] v_account = body["account"]
v_created_at = body["created_at"] v_created_at = body["created_at"]
v_amount = body["amount"] v_amount = body["amount"]
v_account_entry_category = body["account_entry_category"]
return dbInsert(user, token_info, { return dbInsert(user, token_info, {
"statement": """ "statement": """
INSERT INTO account_entry_t INSERT INTO account_entry_t
@ -787,11 +1326,13 @@ INSERT INTO account_entry_t
,account ,account
,created_at ,created_at
,amount ,amount
,account_entry_category
) VALUES ( ) VALUES (
%s %s
,%s ,%s
,%s ,%s
,%s ,%s
,%s
) )
RETURNING * RETURNING *
""", """,
@ -800,6 +1341,7 @@ INSERT INTO account_entry_t
,v_account ,v_account
,v_created_at ,v_created_at
,v_amount ,v_amount
,v_account_entry_category
] ]
}) })
except KeyError as e: except KeyError as e:
@ -816,9 +1358,124 @@ SELECT
,account ,account
,created_at ,created_at
,amount ,amount
,account_entry_category
FROM account_entry_t FROM account_entry_t
WHERE id = %s WHERE id = %s
""", """,
"params": (account_entryId, ) "params": (account_entryId, )
} }
) )
def get_account_entry_by_account(user, token_info, accountId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,account
,created_at
,amount
,account_entry_category
FROM account_entry_t
WHERE account = %s
""",
"params": (accountId, )
}
)
def get_account_entry_by_account_entry_category(user, token_info, account_entry_categoryId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,description
,account
,created_at
,amount
,account_entry_category
FROM account_entry_t
WHERE account_entry_category = %s
""",
"params": (account_entry_categoryId, )
}
)
def get_notes(user, token_info):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,created_at
,tenant
,note
FROM note_t
""",
"params": ()
}
)
def insert_note(user, token_info, **args):
try:
body = args["body"]
v_created_at = body["created_at"]
v_tenant = body["tenant"]
v_note = body["note"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO note_t
(
created_at
,tenant
,note
) VALUES (
%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_created_at
,v_tenant
,v_note
]
})
except KeyError as e:
logger.warning("insert_note: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_note(user, token_info, noteId=None):
return dbGetOne(user, token_info, {
"statement": """
SELECT
id
,created_at
,tenant
,note
FROM note_t
WHERE id = %s
""",
"params": (noteId, )
}
)
def get_note_by_tenant(user, token_info, tenantId=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
,created_at
,tenant
,note
FROM note_t
WHERE tenant = %s
""",
"params": (tenantId, )
}
)

View File

@ -1,7 +1,8 @@
$GENERATED_PYTHON_COMMENT $GENERATED_PYTHON_COMMENT
from db import dbGetMany, dbGetOne, dbInsert from db import dbGetMany, dbGetOne, dbInsert, dbUpdate
from loguru import logger from loguru import logger
import werkzeug
#for $table in $tables #for $table in $tables
def get_${table.name}s(user, token_info): def get_${table.name}s(user, token_info):
@ -77,4 +78,62 @@ SELECT
"params": (${table.name}Id, ) "params": (${table.name}Id, )
} }
) )
#if (('immutable' not in $table) or (not $table.immutable))
def update_${table.name}(user, token_info, ${table.name}Id=None, **args):
try:
body = args["body"]
#for $column in $table.columns
#if (('immutable' not in $column) or (not $column.immutable))
v_$column.name = body["$column.name"]
#end if
#end for
return dbUpdate(user, token_info, {
"statement": """
UPDATE ${table.name}_t
SET
#set $sep=""
#for $column in $table.columns
#if (('immutable' not in $column) or (not $column.immutable))
$sep$column.name = %s
#end if
#set $sep=","
#end for
WHERE id = %s
RETURNING *
""",
"params": [
#for $column in $table.columns
#if (('immutable' not in $column) or (not $column.immutable))
v_${column.name},
#end if
#end for
${table.name}Id
]
})
except KeyError as e:
logger.warning("update_${table.name}: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
#end if
#for $column in $table.columns
#if (('foreignkey' in $column) and $column.foreignkey)
def get_${table.name}_by_${column.name}(user, token_info, ${column.name}Id=None):
return dbGetMany(user, token_info, {
"statement": """
SELECT
id
#for $innerColumn in $table.columns
,$innerColumn.name
#end for
FROM ${table.name}_t
WHERE ${column.name} = %s
""",
"params": (${column.name}Id, )
}
)
#end if
#end for
#end for #end for

View File

@ -78,6 +78,33 @@ paths:
$ref: '#/components/schemas/account' $ref: '#/components/schemas/account'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "account" ]
summary: Update a account
operationId: methods.update_account
parameters:
- name: accountId
in: path
required: true
schema:
type: integer
requestBody:
description: account
content:
application/json:
schema:
$ref: '#/components/schemas/account'
responses:
'200':
description: account successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account'
security:
- jwt: ['secret']
/v1/tenants: /v1/tenants:
get: get:
tags: [ "tenant" ] tags: [ "tenant" ]
@ -137,6 +164,55 @@ paths:
$ref: '#/components/schemas/tenant' $ref: '#/components/schemas/tenant'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "tenant" ]
summary: Update a tenant
operationId: methods.update_tenant
parameters:
- name: tenantId
in: path
required: true
schema:
type: integer
requestBody:
description: tenant
content:
application/json:
schema:
$ref: '#/components/schemas/tenant'
responses:
'200':
description: tenant successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenant'
security:
- jwt: ['secret']
/v1/tenants/account/{accountId}:
get:
tags: [ "tenant", "account" ]
summary: Return tenant by $account
operationId: methods.get_tenant_by_account
parameters:
- name: accountId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenant response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenant'
security:
- jwt: ['secret']
/v1/premises: /v1/premises:
get: get:
tags: [ "premise" ] tags: [ "premise" ]
@ -196,6 +272,33 @@ paths:
$ref: '#/components/schemas/premise' $ref: '#/components/schemas/premise'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "premise" ]
summary: Update a premise
operationId: methods.update_premise
parameters:
- name: premiseId
in: path
required: true
schema:
type: integer
requestBody:
description: premise
content:
application/json:
schema:
$ref: '#/components/schemas/premise'
responses:
'200':
description: premise successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/premise'
security:
- jwt: ['secret']
/v1/flats: /v1/flats:
get: get:
tags: [ "flat" ] tags: [ "flat" ]
@ -255,6 +358,55 @@ paths:
$ref: '#/components/schemas/flat' $ref: '#/components/schemas/flat'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "flat" ]
summary: Update a flat
operationId: methods.update_flat
parameters:
- name: flatId
in: path
required: true
schema:
type: integer
requestBody:
description: flat
content:
application/json:
schema:
$ref: '#/components/schemas/flat'
responses:
'200':
description: flat successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/flat'
security:
- jwt: ['secret']
/v1/flats/premise/{premiseId}:
get:
tags: [ "flat", "premise" ]
summary: Return flat by $premise
operationId: methods.get_flat_by_premise
parameters:
- name: premiseId
in: path
required: true
schema:
type: integer
responses:
'200':
description: flat response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/flat'
security:
- jwt: ['secret']
/v1/overhead_advances: /v1/overhead_advances:
get: get:
tags: [ "overhead_advance" ] tags: [ "overhead_advance" ]
@ -314,6 +466,33 @@ paths:
$ref: '#/components/schemas/overhead_advance' $ref: '#/components/schemas/overhead_advance'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "overhead_advance" ]
summary: Update a overhead_advance
operationId: methods.update_overhead_advance
parameters:
- name: overhead_advanceId
in: path
required: true
schema:
type: integer
requestBody:
description: overhead_advance
content:
application/json:
schema:
$ref: '#/components/schemas/overhead_advance'
responses:
'200':
description: overhead_advance successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance'
security:
- jwt: ['secret']
/v1/overhead_advance_flat_mappings: /v1/overhead_advance_flat_mappings:
get: get:
tags: [ "overhead_advance_flat_mapping" ] tags: [ "overhead_advance_flat_mapping" ]
@ -373,6 +552,50 @@ paths:
$ref: '#/components/schemas/overhead_advance_flat_mapping' $ref: '#/components/schemas/overhead_advance_flat_mapping'
security: security:
- jwt: ['secret'] - jwt: ['secret']
/v1/overhead_advance_flat_mappings/overhead_advance/{overhead_advanceId}:
get:
tags: [ "overhead_advance_flat_mapping", "overhead_advance" ]
summary: Return overhead_advance_flat_mapping by $overhead_advance
operationId: methods.get_overhead_advance_flat_mapping_by_overhead_advance
parameters:
- name: overhead_advanceId
in: path
required: true
schema:
type: integer
responses:
'200':
description: overhead_advance_flat_mapping response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance_flat_mapping'
security:
- jwt: ['secret']
/v1/overhead_advance_flat_mappings/flat/{flatId}:
get:
tags: [ "overhead_advance_flat_mapping", "flat" ]
summary: Return overhead_advance_flat_mapping by $flat
operationId: methods.get_overhead_advance_flat_mapping_by_flat
parameters:
- name: flatId
in: path
required: true
schema:
type: integer
responses:
'200':
description: overhead_advance_flat_mapping response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance_flat_mapping'
security:
- jwt: ['secret']
/v1/parkings: /v1/parkings:
get: get:
tags: [ "parking" ] tags: [ "parking" ]
@ -432,6 +655,55 @@ paths:
$ref: '#/components/schemas/parking' $ref: '#/components/schemas/parking'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "parking" ]
summary: Update a parking
operationId: methods.update_parking
parameters:
- name: parkingId
in: path
required: true
schema:
type: integer
requestBody:
description: parking
content:
application/json:
schema:
$ref: '#/components/schemas/parking'
responses:
'200':
description: parking successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/parking'
security:
- jwt: ['secret']
/v1/parkings/premise/{premiseId}:
get:
tags: [ "parking", "premise" ]
summary: Return parking by $premise
operationId: methods.get_parking_by_premise
parameters:
- name: premiseId
in: path
required: true
schema:
type: integer
responses:
'200':
description: parking response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/parking'
security:
- jwt: ['secret']
/v1/commercial_premises: /v1/commercial_premises:
get: get:
tags: [ "commercial_premise" ] tags: [ "commercial_premise" ]
@ -491,6 +763,55 @@ paths:
$ref: '#/components/schemas/commercial_premise' $ref: '#/components/schemas/commercial_premise'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "commercial_premise" ]
summary: Update a commercial_premise
operationId: methods.update_commercial_premise
parameters:
- name: commercial_premiseId
in: path
required: true
schema:
type: integer
requestBody:
description: commercial_premise
content:
application/json:
schema:
$ref: '#/components/schemas/commercial_premise'
responses:
'200':
description: commercial_premise successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/commercial_premise'
security:
- jwt: ['secret']
/v1/commercial_premises/premise/{premiseId}:
get:
tags: [ "commercial_premise", "premise" ]
summary: Return commercial_premise by $premise
operationId: methods.get_commercial_premise_by_premise
parameters:
- name: premiseId
in: path
required: true
schema:
type: integer
responses:
'200':
description: commercial_premise response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/commercial_premise'
security:
- jwt: ['secret']
/v1/tenancys: /v1/tenancys:
get: get:
tags: [ "tenancy" ] tags: [ "tenancy" ]
@ -550,6 +871,121 @@ paths:
$ref: '#/components/schemas/tenancy' $ref: '#/components/schemas/tenancy'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "tenancy" ]
summary: Update a tenancy
operationId: methods.update_tenancy
parameters:
- name: tenancyId
in: path
required: true
schema:
type: integer
requestBody:
description: tenancy
content:
application/json:
schema:
$ref: '#/components/schemas/tenancy'
responses:
'200':
description: tenancy successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
/v1/tenancys/tenant/{tenantId}:
get:
tags: [ "tenancy", "tenant" ]
summary: Return tenancy by $tenant
operationId: methods.get_tenancy_by_tenant
parameters:
- name: tenantId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
/v1/tenancys/flat/{flatId}:
get:
tags: [ "tenancy", "flat" ]
summary: Return tenancy by $flat
operationId: methods.get_tenancy_by_flat
parameters:
- name: flatId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
/v1/tenancys/parking/{parkingId}:
get:
tags: [ "tenancy", "parking" ]
summary: Return tenancy by $parking
operationId: methods.get_tenancy_by_parking
parameters:
- name: parkingId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
/v1/tenancys/commercial_premise/{commercial_premiseId}:
get:
tags: [ "tenancy", "commercial_premise" ]
summary: Return tenancy by $commercial_premise
operationId: methods.get_tenancy_by_commercial_premise
parameters:
- name: commercial_premiseId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
/v1/fees: /v1/fees:
get: get:
tags: [ "fee" ] tags: [ "fee" ]
@ -609,6 +1045,33 @@ paths:
$ref: '#/components/schemas/fee' $ref: '#/components/schemas/fee'
security: security:
- jwt: ['secret'] - jwt: ['secret']
put:
tags: [ "fee" ]
summary: Update a fee
operationId: methods.update_fee
parameters:
- name: feeId
in: path
required: true
schema:
type: integer
requestBody:
description: fee
content:
application/json:
schema:
$ref: '#/components/schemas/fee'
responses:
'200':
description: fee successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/fee'
security:
- jwt: ['secret']
/v1/tenancy_fee_mappings: /v1/tenancy_fee_mappings:
get: get:
tags: [ "tenancy_fee_mapping" ] tags: [ "tenancy_fee_mapping" ]
@ -668,6 +1131,109 @@ paths:
$ref: '#/components/schemas/tenancy_fee_mapping' $ref: '#/components/schemas/tenancy_fee_mapping'
security: security:
- jwt: ['secret'] - jwt: ['secret']
/v1/tenancy_fee_mappings/tenancy/{tenancyId}:
get:
tags: [ "tenancy_fee_mapping", "tenancy" ]
summary: Return tenancy_fee_mapping by $tenancy
operationId: methods.get_tenancy_fee_mapping_by_tenancy
parameters:
- name: tenancyId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy_fee_mapping response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy_fee_mapping'
security:
- jwt: ['secret']
/v1/tenancy_fee_mappings/fee/{feeId}:
get:
tags: [ "tenancy_fee_mapping", "fee" ]
summary: Return tenancy_fee_mapping by $fee
operationId: methods.get_tenancy_fee_mapping_by_fee
parameters:
- name: feeId
in: path
required: true
schema:
type: integer
responses:
'200':
description: tenancy_fee_mapping response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy_fee_mapping'
security:
- jwt: ['secret']
/v1/account_entry_categorys:
get:
tags: [ "account_entry_category" ]
summary: Return all normalized account_entry_categorys
operationId: methods.get_account_entry_categorys
responses:
'200':
description: account_entry_categorys response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry_category'
security:
- jwt: ['secret']
post:
tags: [ "account_entry_category" ]
summary: Insert a account_entry_category
operationId: methods.insert_account_entry_category
requestBody:
description: account_entry_category
content:
application/json:
schema:
$ref: '#/components/schemas/account_entry_category'
responses:
'200':
description: account_entry_category successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry_category'
security:
- jwt: ['secret']
/v1/account_entry_categorys/{account_entry_categoryId}:
get:
tags: [ "account_entry_category" ]
summary: Return the normalized account_entry_category with given id
operationId: methods.get_account_entry_category
parameters:
- name: account_entry_categoryId
in: path
required: true
schema:
type: integer
responses:
'200':
description: account_entry_category response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry_category'
security:
- jwt: ['secret']
/v1/account_entrys: /v1/account_entrys:
get: get:
tags: [ "account_entry" ] tags: [ "account_entry" ]
@ -727,6 +1293,207 @@ paths:
$ref: '#/components/schemas/account_entry' $ref: '#/components/schemas/account_entry'
security: security:
- jwt: ['secret'] - jwt: ['secret']
/v1/account_entrys/account/{accountId}:
get:
tags: [ "account_entry", "account" ]
summary: Return account_entry by $account
operationId: methods.get_account_entry_by_account
parameters:
- name: accountId
in: path
required: true
schema:
type: integer
responses:
'200':
description: account_entry response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry'
security:
- jwt: ['secret']
/v1/account_entrys/account_entry_category/{account_entry_categoryId}:
get:
tags: [ "account_entry", "account_entry_category" ]
summary: Return account_entry by $account_entry_category
operationId: methods.get_account_entry_by_account_entry_category
parameters:
- name: account_entry_categoryId
in: path
required: true
schema:
type: integer
responses:
'200':
description: account_entry response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry'
security:
- jwt: ['secret']
/v1/notes:
get:
tags: [ "note" ]
summary: Return all normalized notes
operationId: methods.get_notes
responses:
'200':
description: notes response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/note'
security:
- jwt: ['secret']
post:
tags: [ "note" ]
summary: Insert a note
operationId: methods.insert_note
requestBody:
description: note
content:
application/json:
schema:
$ref: '#/components/schemas/note'
responses:
'200':
description: note successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/note'
security:
- jwt: ['secret']
/v1/notes/{noteId}:
get:
tags: [ "note" ]
summary: Return the normalized note with given id
operationId: methods.get_note
parameters:
- name: noteId
in: path
required: true
schema:
type: integer
responses:
'200':
description: note response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/note'
security:
- jwt: ['secret']
/v1/notes/tenant/{tenantId}:
get:
tags: [ "note", "tenant" ]
summary: Return note by $tenant
operationId: methods.get_note_by_tenant
parameters:
- name: tenantId
in: path
required: true
schema:
type: integer
responses:
'200':
description: note response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/note'
security:
- jwt: ['secret']
# -------------------------------------------------------------------
# ATTENTION: This file will not be parsed by Cheetah
# Use plain openapi/yaml syntax, no Cheetah
# escaping
# -------------------------------------------------------------------
/v1/overhead_advances/flat/{flatId}:
get:
tags: [ "overhead_advance", "flat" ]
summary: Return overhead_advances by flat
operationId: additional_methods.get_overhead_advances_by_flat
parameters:
- name: flatId
in: path
required: true
schema:
type: integer
responses:
'200':
description: overhead_advances_by_flat response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance'
security:
- jwt: ['secret']
/v1/fees/tenancy/{tenancyId}:
get:
tags: [ "fee", "tenancy" ]
summary: Return fees by tenancy
operationId: additional_methods.get_fees_by_tenancy
parameters:
- name: tenancyId
in: path
required: true
schema:
type: integer
responses:
'200':
description: get_fees_by_tenancy response
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/fee'
security:
- jwt: ['secret']
/v1/account/saldo/{accountId}:
get:
tags: [ "account" ]
summary: Return saldo of the account
operationId: additional_methods.get_account_saldo
parameters:
- name: accountId
in: path
required: true
schema:
type: integer
responses:
'200':
description: get_account_saldo
content:
'application/json':
schema:
type: object
properties:
saldo:
type: number
security:
- jwt: ['secret']
components: components:
securitySchemes: securitySchemes:
@ -892,6 +1659,16 @@ components:
type: integer type: integer
fee: fee:
type: integer type: integer
account_entry_category:
description: account_entry_category
type: object
properties:
id:
type: integer
description:
type: string
overhead_relevant:
type: boolean
account_entry: account_entry:
description: account_entry description: account_entry
type: object type: object
@ -906,3 +1683,17 @@ components:
type: string type: string
amount: amount:
type: number type: number
account_entry_category:
type: integer
note:
description: note
type: object
properties:
id:
type: integer
created_at:
type: string
tenant:
type: integer
note:
type: string

View File

@ -74,7 +74,64 @@ paths:
\$ref: '#/components/schemas/$table.name' \$ref: '#/components/schemas/$table.name'
security: security:
- jwt: ['secret'] - jwt: ['secret']
#if (('immutable' not in $table) or (not $table.immutable))
put:
tags: [ "$table.name" ]
summary: Update a ${table.name}
operationId: methods.update_${table.name}
parameters:
- name: ${table.name}Id
in: path
required: true
schema:
type: integer
requestBody:
description: $table.name
content:
application/json:
schema:
\$ref: '#/components/schemas/$table.name'
responses:
'200':
description: ${table.name} successfully inserted
content:
'application/json':
schema:
type: array
items:
\$ref: '#/components/schemas/$table.name'
security:
- jwt: ['secret']
#end if
#for $column in $table.columns
#if (('foreignkey' in $column) and $column.foreignkey)
/v1/${table.name}s/${column.name}/{${column.name}Id}:
get:
tags: [ "$table.name", "$column.name" ]
summary: Return $table.name by $$column.name
operationId: methods.get_${table.name}_by_${column.name}
parameters:
- name: ${column.name}Id
in: path
required: true
schema:
type: integer
responses:
'200':
description: $table.name response
content:
'application/json':
schema:
type: array
items:
\$ref: '#/components/schemas/$table.name'
security:
- jwt: ['secret']
#end if
#end for #end for
#end for
#include raw "./api/additional_endpoints.yaml"
components: components:
securitySchemes: securitySchemes:

View File

@ -34,6 +34,9 @@ for table in schema["tables"]:
elif column["sqltype"] == 'timestamp': elif column["sqltype"] == 'timestamp':
column["apitype"] = 'string' column["apitype"] = 'string'
column["jstype"] = 'string' column["jstype"] = 'string'
elif column["sqltype"] == 'boolean':
column["apitype"] = 'boolean'
column["jstype"] = 'boolean'
elif column["sqltype"].startswith('varchar'): elif column["sqltype"].startswith('varchar'):
column["apitype"] = 'string' column["apitype"] = 'string'
column["jstype"] = 'string' column["jstype"] = 'string'

View File

@ -3,7 +3,7 @@
{ {
"name": "account", "name": "account",
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "notnull": true, "selector": 0 } { "name": "description", "sqltype": "varchar(128)", "notnull": true, "unique": true, "selector": 0 }
] ]
}, },
{ {
@ -20,13 +20,16 @@
{ "name": "phone1", "sqltype": "varchar(64)" }, { "name": "phone1", "sqltype": "varchar(64)" },
{ "name": "phone2", "sqltype": "varchar(64)" }, { "name": "phone2", "sqltype": "varchar(64)" },
{ "name": "iban", "sqltype": "varchar(64)" }, { "name": "iban", "sqltype": "varchar(64)" },
{ "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true } { "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true, "immutable": true, "unique": true }
] ] ,
"tableConstraints": [
"unique(firstname, lastname)"
]
}, },
{ {
"name": "premise", "name": "premise",
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 }, { "name": "description", "sqltype": "varchar(128)", "selector": 0, "unique": true },
{ "name": "street", "sqltype": "varchar(128)", "notnull": true }, { "name": "street", "sqltype": "varchar(128)", "notnull": true },
{ "name": "zip", "sqltype": "varchar(10)", "notnull": true }, { "name": "zip", "sqltype": "varchar(10)", "notnull": true },
{ "name": "city", "sqltype": "varchar(128)", "notnull": true } { "name": "city", "sqltype": "varchar(128)", "notnull": true }
@ -39,19 +42,23 @@
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 }, { "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 },
{ "name": "area", "sqltype": "numeric(10,2)", "notnull": true }, { "name": "area", "sqltype": "numeric(10,2)", "notnull": true },
{ "name": "flat_no", "sqltype": "integer" } { "name": "flat_no", "sqltype": "integer" }
],
"tableConstraints": [
"unique(description, premise)"
] ]
}, },
{ {
"name": "overhead_advance", "name": "overhead_advance",
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 }, { "name": "description", "sqltype": "varchar(128)", "selector": 0, "unique": true },
{ "name": "amount", "sqltype": "numeric(10,4)", "notnull": true }, { "name": "amount", "sqltype": "numeric(10,4)", "notnull": true, "immutable": true },
{ "name": "startdate", "sqltype": "date", "selector": 1 }, { "name": "startdate", "sqltype": "date", "selector": 1, "immutable": true },
{ "name": "enddate", "sqltype": "date" } { "name": "enddate", "sqltype": "date" }
] ]
}, },
{ {
"name": "overhead_advance_flat_mapping", "name": "overhead_advance_flat_mapping",
"immutable": true,
"columns": [ "columns": [
{ "name": "overhead_advance", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 0 }, { "name": "overhead_advance", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 0 },
{ "name": "flat", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 1 } { "name": "flat", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 1 }
@ -62,6 +69,9 @@
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 1 }, { "name": "description", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 } { "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 }
],
"tableConstraints": [
"unique(description, premise)"
] ]
}, },
{ {
@ -69,50 +79,77 @@
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 1 }, { "name": "description", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 } { "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 }
],
"tableConstraints": [
"unique(description, premise)"
] ]
}, },
{ {
"name": "tenancy", "name": "tenancy",
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 }, { "name": "description", "sqltype": "varchar(128)", "selector": 0, "unique": true },
{ "name": "tenant", "sqltype": "integer", "notnull": true, "foreignkey": true }, { "name": "tenant", "sqltype": "integer", "notnull": true, "foreignkey": true, "immutable": true },
{ "name": "flat", "sqltype": "integer", "notnull": false, "foreignkey": true }, { "name": "flat", "sqltype": "integer", "notnull": false, "foreignkey": true, "immutable": true },
{ "name": "parking", "sqltype": "integer", "notnull": false, "foreignkey": true }, { "name": "parking", "sqltype": "integer", "notnull": false, "foreignkey": true, "immutable": true },
{ "name": "commercial_premise", "sqltype": "integer", "notnull": false, "foreignkey": true }, { "name": "commercial_premise", "sqltype": "integer", "notnull": false, "foreignkey": true, "immutable": true },
{ "name": "startdate", "sqltype": "date", "notnull": true, "selector": 1 }, { "name": "startdate", "sqltype": "date", "notnull": true, "selector": 1, "immutable": true },
{ "name": "enddate", "sqltype": "date", "notnull": false } { "name": "enddate", "sqltype": "date", "notnull": false }
], ],
"tableConstraints": [ "tableConstraints": [
"constraint tenancy_only_one_object check ((flat is not null and parking is null and commercial_premise is null) or (flat is null and parking is not null and commercial_premise is null) or (flat is null and parking is null and commercial_premise is not null))" "check ((flat is not null and parking is null and commercial_premise is null) or (flat is null and parking is not null and commercial_premise is null) or (flat is null and parking is null and commercial_premise is not null))",
"unique(flat, parking, commercial_premise, startdate)"
] ]
}, },
{ {
"name": "fee", "name": "fee",
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 }, { "name": "description", "sqltype": "varchar(128)", "selector": 0, "unique": true },
{ "name": "amount", "sqltype": "numeric(10,2)", "notnull": true }, { "name": "amount", "sqltype": "numeric(10,2)", "notnull": true, "immutable": true },
{ "name": "fee_type", "sqltype": "varchar(10)", "notnull": true }, { "name": "fee_type", "sqltype": "varchar(10)", "notnull": true, "immutable": true },
{ "name": "startdate", "sqltype": "date", "selector": 1 }, { "name": "startdate", "sqltype": "date", "selector": 1, "immutable": true },
{ "name": "enddate", "sqltype": "date" } { "name": "enddate", "sqltype": "date" }
], ],
"tableConstraints": [ "tableConstraints": [
"constraint fee_fee_type check (fee_type = 'per_area' or fee_type = 'total')" "check (fee_type = 'per_area' or fee_type = 'total')"
] ]
}, },
{ {
"name": "tenancy_fee_mapping", "name": "tenancy_fee_mapping",
"immutable": true,
"columns": [ "columns": [
{ "name": "tenancy", "sqltype": "integer", "notnull": true, "foreignkey": true }, { "name": "tenancy", "sqltype": "integer", "notnull": true, "foreignkey": true },
{ "name": "fee", "sqltype": "integer", "notnull": true, "foreignkey": true } { "name": "fee", "sqltype": "integer", "notnull": true, "foreignkey": true }
] ]
}, },
{
"name": "account_entry_category",
"immutable": true,
"columns": [
{ "name": "description", "sqltype": "varchar(128)", "notnull": true, "selector": 0, "unique": true },
{ "name": "overhead_relevant", "sqltype": "boolean", "notnull": true, "default": "true" }
]
},
{ {
"name": "account_entry", "name": "account_entry",
"immutable": true,
"columns": [ "columns": [
{ "name": "description", "sqltype": "varchar(128)", "notnull": true }, { "name": "description", "sqltype": "varchar(128)", "notnull": true },
{ "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true }, { "name": "account", "sqltype": "integer", "notnull": true, "foreignkey": true },
{ "name": "created_at", "sqltype": "timestamp", "notnull": true, "default": "now()" }, { "name": "created_at", "sqltype": "timestamp", "notnull": true, "default": "now()" },
{ "name": "amount", "sqltype": "numeric(10,2)", "notnull": true } { "name": "amount", "sqltype": "numeric(10,2)", "notnull": true, "selector": 0 },
{ "name": "account_entry_category", "sqltype": "integer", "notnull": true, "foreignkey": true }
],
"tableConstraints": [
"unique(description, account, created_at)"
]
},
{
"name": "note",
"immutable": true,
"columns": [
{ "name": "created_at", "sqltype": "timestamp", "notnull": true, "default": "now()" },
{ "name": "tenant", "sqltype": "integer", "notnull": true, "foreignkey": true },
{ "name": "note", "sqltype": "varchar(4096)", "notnull": true }
] ]
} }
] ]

View File

@ -7,9 +7,12 @@
CREATE TABLE account_t ( CREATE TABLE account_t (
id serial not null primary key id serial not null primary key
,description varchar(128) not null ,description varchar(128) not null unique
); );
GRANT SELECT, INSERT, UPDATE ON account_t TO hv2;
GRANT SELECT, UPDATE ON account_t_id_seq TO hv2;
CREATE TABLE tenant_t ( CREATE TABLE tenant_t (
id serial not null primary key id serial not null primary key
,salutation varchar(128) ,salutation varchar(128)
@ -23,86 +26,146 @@ CREATE TABLE tenant_t (
,phone1 varchar(64) ,phone1 varchar(64)
,phone2 varchar(64) ,phone2 varchar(64)
,iban varchar(64) ,iban varchar(64)
,account integer not null references account_t (id) ,account integer not null references account_t (id) unique
,unique(firstname, lastname)
); );
GRANT SELECT, INSERT, UPDATE ON tenant_t TO hv2;
GRANT SELECT, UPDATE ON tenant_t_id_seq TO hv2;
CREATE TABLE premise_t ( CREATE TABLE premise_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128) unique
,street varchar(128) not null ,street varchar(128) not null
,zip varchar(10) not null ,zip varchar(10) not null
,city varchar(128) not null ,city varchar(128) not null
); );
GRANT SELECT, INSERT, UPDATE ON premise_t TO hv2;
GRANT SELECT, UPDATE ON premise_t_id_seq TO hv2;
CREATE TABLE flat_t ( CREATE TABLE flat_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128)
,premise integer references premise_t (id) ,premise integer references premise_t (id)
,area numeric(10,2) not null ,area numeric(10,2) not null
,flat_no integer ,flat_no integer
,unique(description, premise)
); );
GRANT SELECT, INSERT, UPDATE ON flat_t TO hv2;
GRANT SELECT, UPDATE ON flat_t_id_seq TO hv2;
CREATE TABLE overhead_advance_t ( CREATE TABLE overhead_advance_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128) unique
,amount numeric(10,4) not null ,amount numeric(10,4) not null
,startdate date ,startdate date
,enddate date ,enddate date
); );
GRANT SELECT, INSERT, UPDATE ON overhead_advance_t TO hv2;
GRANT SELECT, UPDATE ON overhead_advance_t_id_seq TO hv2;
CREATE TABLE overhead_advance_flat_mapping_t ( CREATE TABLE overhead_advance_flat_mapping_t (
id serial not null primary key id serial not null primary key
,overhead_advance integer not null references overhead_advance_t (id) ,overhead_advance integer not null references overhead_advance_t (id)
,flat integer not null references flat_t (id) ,flat integer not null references flat_t (id)
); );
GRANT SELECT, INSERT ON overhead_advance_flat_mapping_t TO hv2;
GRANT SELECT, UPDATE ON overhead_advance_flat_mapping_t_id_seq TO hv2;
CREATE TABLE parking_t ( CREATE TABLE parking_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128)
,premise integer references premise_t (id) ,premise integer references premise_t (id)
,unique(description, premise)
); );
GRANT SELECT, INSERT, UPDATE ON parking_t TO hv2;
GRANT SELECT, UPDATE ON parking_t_id_seq TO hv2;
CREATE TABLE commercial_premise_t ( CREATE TABLE commercial_premise_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128)
,premise integer references premise_t (id) ,premise integer references premise_t (id)
,unique(description, premise)
); );
GRANT SELECT, INSERT, UPDATE ON commercial_premise_t TO hv2;
GRANT SELECT, UPDATE ON commercial_premise_t_id_seq TO hv2;
CREATE TABLE tenancy_t ( CREATE TABLE tenancy_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128) unique
,tenant integer not null references tenant_t (id) ,tenant integer not null references tenant_t (id)
,flat integer references flat_t (id) ,flat integer references flat_t (id)
,parking integer references parking_t (id) ,parking integer references parking_t (id)
,commercial_premise integer references commercial_premise_t (id) ,commercial_premise integer references commercial_premise_t (id)
,startdate date not null ,startdate date not null
,enddate date ,enddate date
,constraint tenancy_only_one_object check ((flat is not null and parking is null and commercial_premise is null) or (flat is null and parking is not null and commercial_premise is null) or (flat is null and parking is null and commercial_premise is not null)) ,check ((flat is not null and parking is null and commercial_premise is null) or (flat is null and parking is not null and commercial_premise is null) or (flat is null and parking is null and commercial_premise is not null))
,unique(flat, parking, commercial_premise, startdate)
); );
GRANT SELECT, INSERT, UPDATE ON tenancy_t TO hv2;
GRANT SELECT, UPDATE ON tenancy_t_id_seq TO hv2;
CREATE TABLE fee_t ( CREATE TABLE fee_t (
id serial not null primary key id serial not null primary key
,description varchar(128) ,description varchar(128) unique
,amount numeric(10,2) not null ,amount numeric(10,2) not null
,fee_type varchar(10) not null ,fee_type varchar(10) not null
,startdate date ,startdate date
,enddate date ,enddate date
,constraint fee_fee_type check (fee_type = 'per_area' or fee_type = 'total') ,check (fee_type = 'per_area' or fee_type = 'total')
); );
GRANT SELECT, INSERT, UPDATE ON fee_t TO hv2;
GRANT SELECT, UPDATE ON fee_t_id_seq TO hv2;
CREATE TABLE tenancy_fee_mapping_t ( CREATE TABLE tenancy_fee_mapping_t (
id serial not null primary key id serial not null primary key
,tenancy integer not null references tenancy_t (id) ,tenancy integer not null references tenancy_t (id)
,fee integer not null references fee_t (id) ,fee integer not null references fee_t (id)
); );
GRANT SELECT, INSERT ON tenancy_fee_mapping_t TO hv2;
GRANT SELECT, UPDATE ON tenancy_fee_mapping_t_id_seq TO hv2;
CREATE TABLE account_entry_category_t (
id serial not null primary key
,description varchar(128) not null unique
,overhead_relevant boolean not null default true
);
GRANT SELECT, INSERT ON account_entry_category_t TO hv2;
GRANT SELECT, UPDATE ON account_entry_category_t_id_seq TO hv2;
CREATE TABLE account_entry_t ( CREATE TABLE account_entry_t (
id serial not null primary key id serial not null primary key
,description varchar(128) not null ,description varchar(128) not null
,account integer not null references account_t (id) ,account integer not null references account_t (id)
,created_at timestamp not null default now() ,created_at timestamp not null default now()
,amount numeric(10,2) not null ,amount numeric(10,2) not null
,account_entry_category integer not null references account_entry_category_t (id)
,unique(description, account, created_at)
); );
GRANT SELECT, INSERT ON account_entry_t TO hv2;
GRANT SELECT, UPDATE ON account_entry_t_id_seq TO hv2;
CREATE TABLE note_t (
id serial not null primary key
,created_at timestamp not null default now()
,tenant integer not null references tenant_t (id)
,note varchar(4096) not null
);
GRANT SELECT, INSERT ON note_t TO hv2;
GRANT SELECT, UPDATE ON note_t_id_seq TO hv2;

View File

@ -14,6 +14,9 @@ CREATE TABLE ${table.name}_t (
#if (('foreignkey' in $column) and $column.foreignkey) #if (('foreignkey' in $column) and $column.foreignkey)
references ${column.name}_t (id) #slurp references ${column.name}_t (id) #slurp
#end if #end if
#if (('unique' in $column) and $column.unique)
unique #slurp
#end if
#if ('default' in $column) #if ('default' in $column)
default $column.default #slurp default $column.default #slurp
#end if #end if
@ -26,6 +29,14 @@ CREATE TABLE ${table.name}_t (
#end if #end if
); );
GRANT SELECT, INSERT#slurp
#if (('immutable' not in $table) or (not $table.immutable))
, UPDATE#slurp
#end if
ON ${table.name}_t TO hv2;
GRANT SELECT, UPDATE ON ${table.name}_t_id_seq TO hv2;
#end for #end for

View File

@ -460,6 +460,141 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
} }
}, },
"@angular/localize": {
"version": "11.0.9",
"resolved": "https://registry.npmjs.org/@angular/localize/-/localize-11.0.9.tgz",
"integrity": "sha512-5NtyqCcBN8G6muXpyrHuVHTdD9slpyUAl6vF6NbyejgVeuV35wPXwIa+3qauPiVlFGEBQpn/uKAU57mVGm8WUg==",
"dev": true,
"requires": {
"@babel/core": "7.8.3",
"glob": "7.1.2",
"yargs": "^16.1.1"
},
"dependencies": {
"@babel/core": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.3.tgz",
"integrity": "sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@babel/generator": "^7.8.3",
"@babel/helpers": "^7.8.3",
"@babel/parser": "^7.8.3",
"@babel/template": "^7.8.3",
"@babel/traverse": "^7.8.3",
"@babel/types": "^7.8.3",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.0",
"lodash": "^4.17.13",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
}
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true
},
"yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
}
},
"yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true
}
}
},
"@angular/material": { "@angular/material": {
"version": "11.2.13", "version": "11.2.13",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-11.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/material/-/material-11.2.13.tgz",
@ -468,6 +603,14 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
} }
}, },
"@angular/material-moment-adapter": {
"version": "11.2.13",
"resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-11.2.13.tgz",
"integrity": "sha512-KDD7QcfePpwvPG3HYy4kA8Ju222fUZeoyGXxFVE98KgaIIHrTRIGPFLHbpzxIl+AYVgJcW2OiIXKLI09FiYirg==",
"requires": {
"tslib": "^2.0.0"
}
},
"@angular/platform-browser": { "@angular/platform-browser": {
"version": "11.0.9", "version": "11.0.9",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.9.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.9.tgz",
@ -7618,6 +7761,11 @@
"minimist": "^1.2.5" "minimist": "^1.2.5"
} }
}, },
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

View File

@ -19,10 +19,12 @@
"@angular/core": "~11.0.6", "@angular/core": "~11.0.6",
"@angular/forms": "~11.0.6", "@angular/forms": "~11.0.6",
"@angular/material": "^11.2.13", "@angular/material": "^11.2.13",
"@angular/material-moment-adapter": "^11.2.13",
"@angular/platform-browser": "~11.0.6", "@angular/platform-browser": "~11.0.6",
"@angular/platform-browser-dynamic": "~11.0.6", "@angular/platform-browser-dynamic": "~11.0.6",
"@angular/router": "~11.0.6", "@angular/router": "~11.0.6",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"moment": "^2.29.1",
"rxjs": "~6.6.0", "rxjs": "~6.6.0",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"zone.js": "~0.10.2" "zone.js": "~0.10.2"
@ -31,6 +33,7 @@
"@angular-devkit/build-angular": "~0.1100.6", "@angular-devkit/build-angular": "~0.1100.6",
"@angular/cli": "~11.0.6", "@angular/cli": "~11.0.6",
"@angular/compiler-cli": "~11.0.6", "@angular/compiler-cli": "~11.0.6",
"@angular/localize": "^11.0.9",
"@types/jasmine": "~3.6.0", "@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1", "@types/node": "^12.11.1",
"codelyzer": "^6.0.0", "codelyzer": "^6.0.0",

View File

@ -0,0 +1,33 @@
table {
width: 75%;
border-spacing: 20px;
}
.spacer {
flex: 1 1 auto;
}
#addEntryfield {
margin-right: 15px;
}
#divider {
margin-top: 20px;
}
#firstblock {
margin-bottom: 20px;
}
#secondblock {
margin-top: 20px;
}
.rightaligned {
text-align: right;
}
.large {
font-size: large;
}

View File

@ -0,0 +1,76 @@
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{account?.description}} ({{account?.id}})
</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-accordion>
<mat-expansion-panel (opened)="collapse = true"
(closed)="collapse = false">
<mat-expansion-panel-header>
<mat-panel-title *ngIf="!collapse">
Kontoübersicht, Saldo: {{saldo?.saldo | number:'1.2-2'}} €
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
<div id="firstBlock">
<form (ngSubmit)="addAccountEntry()">
<mat-form-field appearance="outline" id="addEntryfield">
<mat-label>Datum</mat-label>
<input matInput name="createdAt" [(ngModel)]="newAccountEntry.created_at" [matDatepicker]="createdAtPicker"/>
<mat-datepicker-toggle matSuffix [for]="createdAtPicker"></mat-datepicker-toggle>
<mat-datepicker #createdAtPicker></mat-datepicker>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Kategorie</mat-label>
<mat-select [(ngModel)]="newAccountEntry.account_entry_category" name="category" disabled="shallBeRentPayment">
<mat-option *ngFor="let p of accountEntryCategories" [value]="p.id">{{p.description}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Betrag (€)</mat-label>
<input matInput type="number" name="amount" [(ngModel)]="newAccountEntry.amount"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="newAccountEntry.description"/>
</mat-form-field>
<button #addAccountEntryButton type="submit" mat-raised-button color="primary">Buchung speichern</button>
</form>
</div>
<div class="large">
Saldo: {{saldo?.saldo | number:'1.2-2'}} €
</div>
<div id="secondBlock">
<table mat-table [dataSource]="accountEntriesDataSource" #zftable>
<ng-container matColumnDef="createdAt">
<th mat-header-cell *matHeaderCellDef>Datum</th>
<td mat-cell *matCellDef="let element">{{element.rawAccountEntry.created_at | date}}</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.rawAccountEntry.description}}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Betrag</th>
<td mat-cell *matCellDef="let element" class="rightaligned">{{element.rawAccountEntry.amount | number:'1.2-2'}} €</td>
</ng-container>
<ng-container matColumnDef="category">
<th mat-header-cell *matHeaderCellDef>Kategorie</th>
<td mat-cell *matCellDef="let element">{{element.accountEntryCategory}}</td>
</ng-container>
<ng-container matColumnDef="overhead_relevant">
<th mat-header-cell *matHeaderCellDef>BK relevant</th>
<td mat-cell *matCellDef="let element">{{element.overheadRelevant}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="accountEntriesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: accountEntriesDisplayedColumns;"></tr>
</table>
</div>
</mat-expansion-panel>
</mat-accordion>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AccountComponent } from './account.component';
describe('AccountComponent', () => {
let component: AccountComponent;
let fixture: ComponentFixture<AccountComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AccountComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AccountComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,139 @@
import { Component, Input, OnInit, OnChanges, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatTableDataSource } from '@angular/material/table';
import { AccountEntryCategoryService, AccountEntryService, AccountService } from '../data-object-service';
import { Account, AccountEntry, AccountEntryCategory, NULL_AccountEntry } from '../data-objects';
import { ExtApiService } from '../ext-data-object-service';
import { Saldo } from '../ext-data-objects';
import { MessageService } from '../message.service';
interface DN_AccountEntry {
rawAccountEntry: AccountEntry
accountEntryCategory: string
overheadRelevant: boolean
}
@Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css']
})
export class AccountComponent implements OnInit {
@Input() selectedAccountId: number
@Input() shallBeRentPayment: boolean
@ViewChild('addAccountEntryButton') addAccountEntryButton: MatButton
collapse: boolean = false
account: Account
accountEntries: DN_AccountEntry[]
accountEntriesDataSource: MatTableDataSource<DN_AccountEntry>
accountEntriesDisplayedColumns: string[] = [ "description", "amount", "createdAt", "category", "overhead_relevant" ]
saldo: Saldo
accountEntryCategories: AccountEntryCategory[]
accountEntryCategoriesMap: Map<number, AccountEntryCategory>
accountEntryCategoriesInverseMap: Map<string, AccountEntryCategory>
newAccountEntry: AccountEntry = NULL_AccountEntry
constructor(
private accountService: AccountService,
private accountEntryService: AccountEntryService,
private extApiService: ExtApiService,
private accountEntryCategoryService: AccountEntryCategoryService,
private messageService: MessageService
) { }
async getAccount(): Promise<void> {
try {
if (this.selectedAccountId) {
this.messageService.add(`Trying to load account ${this.selectedAccountId} and entries`)
this.account = await this.accountService.getAccount(this.selectedAccountId)
this.messageService.add(`Account: ${JSON.stringify(this.account, undefined, 4)}`)
this.getAccountEntries()
}
} catch (err) {
this.messageService.add(`Error in getAccount: ${JSON.stringify(err, undefined, 4)}`)
}
}
async getAccountEntries(): Promise<void> {
try {
const rawAccountEntries = await this.accountEntryService.getAccountEntrysByAccount(this.selectedAccountId)
rawAccountEntries.reverse()
this.messageService.add(`AccountEntries: ${JSON.stringify(rawAccountEntries, undefined, 4)}`)
this.accountEntries = []
for (let f of rawAccountEntries) {
this.accountEntries.push({
rawAccountEntry: f,
accountEntryCategory: this.accountEntryCategoriesMap.get(f.account_entry_category).description,
overheadRelevant: this.accountEntryCategoriesMap.get(f.account_entry_category).overhead_relevant
})
}
this.accountEntriesDataSource = new MatTableDataSource<DN_AccountEntry>(this.accountEntries)
this.saldo = await this.extApiService.getAccountSaldo(this.selectedAccountId)
} catch (err) {
throw err
this.messageService.add(`Error in getAccountEntries: ${JSON.stringify(err, undefined, 4)}`)
}
}
async addAccountEntry(): Promise<void> {
try {
this.addAccountEntryButton.disabled = true
this.newAccountEntry.account = this.account.id
this.messageService.add(`addAccountEntry: ${ JSON.stringify(this.newAccountEntry, undefined, 4) }`)
this.newAccountEntry = await this.accountEntryService.postAccountEntry(this.newAccountEntry)
this.messageService.add(`New accountEntry created: ${this.newAccountEntry.id}`)
this.newAccountEntry = { 'account': this.account.id, 'amount': undefined, 'created_at': '', 'description': '', 'id': 0, 'account_entry_category': 0 }
this.getAccountEntries()
} catch (err) {
this.messageService.add(`Error in addAccountEntry: ${JSON.stringify(err, undefined, 4)}`)
} finally {
this.addAccountEntryButton.disabled = false
}
}
async getAccountEntryCategories(): Promise<void> {
try {
this.accountEntryCategories = await this.accountEntryCategoryService.getAccountEntryCategorys()
this.accountEntryCategoriesMap = new Map<number, AccountEntryCategory>()
this.accountEntryCategoriesInverseMap = new Map<string, AccountEntryCategory>()
for (let p of this.accountEntryCategories) {
this.accountEntryCategoriesMap.set(p.id, p)
this.accountEntryCategoriesInverseMap.set(p.description, p)
}
this.messageService.add(`getAccountEntryCategories: ${JSON.stringify(this.accountEntryCategories, undefined, 4)}`)
} catch (err) {
this.messageService.add(`Error in getAccountEntryCategories: ${JSON.stringify(err, undefined, 4)}`)
}
}
private async init(): Promise<void> {
this.messageService.add(`AccountComponent.init, account: ${this.selectedAccountId}`)
this.getAccount()
await this.getAccountEntryCategories()
if (this.shallBeRentPayment) {
this.messageService.add('shall be rentpayment')
this.newAccountEntry.account_entry_category = this.accountEntryCategoriesInverseMap.get('Mietzahlung').id
}
}
ngOnInit(): void {
this.init()
}
ngOnChanges(): void {
this.init()
}
}

View File

@ -11,6 +11,12 @@ import { MyCommercialUnitsComponent } from './my-commercial-units/my-commercial-
import { TenantDetailsComponent } from './tenant-details/tenant-details.component'; import { TenantDetailsComponent } from './tenant-details/tenant-details.component';
import { PremiseDetailsComponent } from './premise-details/premise-details.component'; import { PremiseDetailsComponent } from './premise-details/premise-details.component';
import { FlatDetailsComponent } from './flat-details/flat-details.component'; import { FlatDetailsComponent } from './flat-details/flat-details.component';
import { ParkingDetailsComponent } from './parking-details/parking-details.component';
import { CommercialUnitDetailsComponent } from './commercial-unit-details/commercial-unit-details.component';
import { OverheadAdvanceListComponent } from './overhead-advance-list/overhead-advance-list.component';
import { OverheadAdvanceDetailsComponent } from './overhead-advance-details/overhead-advance-details.component';
import { FeeListComponent } from './fee-list/fee-list.component';
import { FeeDetailsComponent } from './fee-details/fee-details.component';
const routes: Routes = [ const routes: Routes = [
@ -25,6 +31,16 @@ const routes: Routes = [
{ path: 'premise', component: PremiseDetailsComponent, canActivate: [ AuthGuardService ] }, { path: 'premise', component: PremiseDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'flat/:id', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] }, { path: 'flat/:id', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'flat', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] }, { path: 'flat', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'parking/:id', component: ParkingDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'parking', component: ParkingDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'commercialunit/:id', component: CommercialUnitDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'commercialunit', component: CommercialUnitDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'overheadadvances', component: OverheadAdvanceListComponent, canActivate: [ AuthGuardService ] },
{ path: 'overheadadvance/:id', component: OverheadAdvanceDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'overheadadvance', component: OverheadAdvanceDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'fees', component: FeeListComponent, canActivate: [ AuthGuardService ] },
{ path: 'fee/:id', component: FeeDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'fee', component: FeeDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'logout', component: LogoutComponent }, { path: 'logout', component: LogoutComponent },
{ path: 'login', component: LoginComponent } { path: 'login', component: LoginComponent }
] ]

View File

@ -1,5 +1,7 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; import { LOCALE_ID, NgModule } from '@angular/core';
import localeDe from '@angular/common/locales/de';
import { registerLocaleData } from '@angular/common';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -31,7 +33,22 @@ import { MyCommercialUnitsComponent } from './my-commercial-units/my-commercial-
import { TenantDetailsComponent } from './tenant-details/tenant-details.component'; import { TenantDetailsComponent } from './tenant-details/tenant-details.component';
import { PremiseDetailsComponent } from './premise-details/premise-details.component'; import { PremiseDetailsComponent } from './premise-details/premise-details.component';
import { FlatDetailsComponent } from './flat-details/flat-details.component'; import { FlatDetailsComponent } from './flat-details/flat-details.component';
import { MatSelectModule } from '@angular/material/select' import { MatSelectModule } from '@angular/material/select';
import { ParkingDetailsComponent } from './parking-details/parking-details.component';
import { CommercialUnitDetailsComponent } from './commercial-unit-details/commercial-unit-details.component';
import { OverheadAdvanceListComponent } from './overhead-advance-list/overhead-advance-list.component';
import { OverheadAdvanceDetailsComponent } from './overhead-advance-details/overhead-advance-details.component'
import { MatDatepickerModule } from '@angular/material/datepicker'
import { MatNativeDateModule } from '@angular/material/core';
import { FeeListComponent } from './fee-list/fee-list.component';
import { FeeDetailsComponent } from './fee-details/fee-details.component';
import { MatExpansionModule } from '@angular/material/expansion';
import { AccountComponent } from './account/account.component';
import { NoteComponent } from './note/note.component'
import { MatMomentDateModule, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter'
registerLocaleData(localeDe)
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
@ -47,7 +64,15 @@ import { MatSelectModule } from '@angular/material/select'
MyCommercialUnitsComponent, MyCommercialUnitsComponent,
TenantDetailsComponent, TenantDetailsComponent,
PremiseDetailsComponent, PremiseDetailsComponent,
FlatDetailsComponent FlatDetailsComponent,
ParkingDetailsComponent,
CommercialUnitDetailsComponent,
OverheadAdvanceListComponent,
OverheadAdvanceDetailsComponent,
FeeListComponent,
FeeDetailsComponent,
AccountComponent,
NoteComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -66,11 +91,19 @@ import { MatSelectModule } from '@angular/material/select'
MatTableModule, MatTableModule,
MatInputModule, MatInputModule,
MatFormFieldModule, MatFormFieldModule,
MatSelectModule MatSelectModule,
MatDatepickerModule,
MatNativeDateModule,
MatExpansionModule
],
exports: [
MatMomentDateModule
], ],
providers: [ providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: AuthHandlerInterceptor, multi: true } { provide: HTTP_INTERCEPTORS, useClass: AuthHandlerInterceptor, multi: true },
{ provide: LOCALE_ID, useValue: 'de' },
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -17,8 +17,12 @@ export class AuthHandlerInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService, private messageService: MessageService) {} constructor(private tokenService: TokenService, private messageService: MessageService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const token = localStorage.getItem(TokenService.Id_Token_Key) const token = localStorage.getItem(TokenService.Id_AuthToken_Key)
if (request.url.includes(serviceBaseUrl) && token) { if (request.url.includes(serviceBaseUrl) && token) {
this.messageService.add("start refresh of tokens")
this.tokenService.refresh()
this.messageService.add("api request intercepted")
const clone = request.clone({ const clone = request.clone({
setHeaders: { Authorization: `Bearer ${token}`} setHeaders: { Authorization: `Bearer ${token}`}
}) })

View File

@ -0,0 +1,31 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{commercialPremise?.description}} {{premise?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{commercialPremise?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveCommercialPremise()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="commercialPremise.description"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Haus</mat-label>
<mat-select [(ngModel)]="commercialPremise.premise" name="premise">
<mat-option *ngFor="let p of premises" [value]="p.id">{{p.description}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
</form>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CommercialUnitDetailsComponent } from './commercial-unit-details.component';
describe('CommercialUnitDetailsComponent', () => {
let component: CommercialUnitDetailsComponent;
let fixture: ComponentFixture<CommercialUnitDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CommercialUnitDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CommercialUnitDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,79 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { CommercialPremiseService, PremiseService } from '../data-object-service';
import { NULL_CommercialPremise, NULL_Premise, CommercialPremise, Premise } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-commercial-unit-details',
templateUrl: './commercial-unit-details.component.html',
styleUrls: ['./commercial-unit-details.component.css']
})
export class CommercialUnitDetailsComponent implements OnInit {
@ViewChild('submitButton') submitButton: MatButton
commercialPremise: CommercialPremise = NULL_CommercialPremise
premise: Premise = NULL_Premise
premises: Premise[]
constructor(
private commercialPremiseService: CommercialPremiseService,
private premiseService: PremiseService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getCommercialPremise(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) {
this.commercialPremise = await this.commercialPremiseService.getCommercialPremise(id)
this.premise = await this.premiseService.getPremise(this.commercialPremise.premise)
}
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async getPremises(): Promise<void> {
try {
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add("Premises loaded")
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async saveCommercialPremise() {
try {
this.submitButton.disabled = true
this.messageService.add(`saveCommercialPremise: ${ JSON.stringify(this.commercialPremise, undefined, 4) }`)
if (this.commercialPremise.id == 0) {
this.messageService.add("about to insert new commercialPremise")
this.commercialPremise = await this.commercialPremiseService.postCommercialPremise(this.commercialPremise)
this.messageService.add(`Successfully added commercialPremise with id ${this.commercialPremise.id}`)
} else {
this.messageService.add("about to update existing commercialPremise")
this.commercialPremise = await this.commercialPremiseService.putCommercialPremise(this.commercialPremise)
this.messageService.add(`Successfully changed commercialPremise with id ${this.commercialPremise.id}`)
}
this.router.navigate(['/commercialunits'])
} finally {
this.submitButton.disabled = false
}
}
ngOnInit(): void {
this.getPremises()
this.getCommercialPremise()
}
}

View File

@ -1,4 +1,5 @@
// export const serviceBaseUrl = "https://api.hv.nober.de"; // export const serviceBaseUrl = "https://api.hv.nober.de";
// export const serviceBaseUrl = "http://172.16.10.38:5000"; // export const serviceBaseUrl = "http://172.16.10.38:5000";
export const serviceBaseUrl = "http://localhost:8080"; export const serviceBaseUrl = "http://localhost:8080"
export const authserviceBaseUrl = "https://authservice.hottis.de"
export const applicationId = "hv2"

View File

@ -7,8 +7,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs'; import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MessageService } from './message.service'; import { MessageService } from './message.service';
import { serviceBaseUrl } from './config'; import { serviceBaseUrl } from './config';
@ -25,7 +24,9 @@ import { CommercialPremise } from './data-objects';
import { Tenancy } from './data-objects'; import { Tenancy } from './data-objects';
import { Fee } from './data-objects'; import { Fee } from './data-objects';
import { TenancyFeeMapping } from './data-objects'; import { TenancyFeeMapping } from './data-objects';
import { AccountEntryCategory } from './data-objects';
import { AccountEntry } from './data-objects'; import { AccountEntry } from './data-objects';
import { Note } from './data-objects';
@ -54,6 +55,15 @@ export class AccountService {
return this.http.post<Account>(`${serviceBaseUrl}/v1/accounts`, item).toPromise() return this.http.post<Account>(`${serviceBaseUrl}/v1/accounts`, item).toPromise()
} }
async putAccount(item: Account): Promise<Account> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`AccountService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Account>(`${serviceBaseUrl}/v1/accounts/${id}`, item).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -76,6 +86,20 @@ export class TenantService {
return this.http.post<Tenant>(`${serviceBaseUrl}/v1/tenants`, item).toPromise() return this.http.post<Tenant>(`${serviceBaseUrl}/v1/tenants`, item).toPromise()
} }
async putTenant(item: Tenant): Promise<Tenant> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`TenantService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Tenant>(`${serviceBaseUrl}/v1/tenants/${id}`, item).toPromise()
}
async getTenantsByAccount(id: number): Promise<Tenant[]> {
this.messageService.add(`TenantService: get data by Account ${id}`);
return this.http.get<Tenant[]>(`${serviceBaseUrl}/v1/tenants/account/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -98,6 +122,15 @@ export class PremiseService {
return this.http.post<Premise>(`${serviceBaseUrl}/v1/premises`, item).toPromise() return this.http.post<Premise>(`${serviceBaseUrl}/v1/premises`, item).toPromise()
} }
async putPremise(item: Premise): Promise<Premise> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`PremiseService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Premise>(`${serviceBaseUrl}/v1/premises/${id}`, item).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -120,6 +153,20 @@ export class FlatService {
return this.http.post<Flat>(`${serviceBaseUrl}/v1/flats`, item).toPromise() return this.http.post<Flat>(`${serviceBaseUrl}/v1/flats`, item).toPromise()
} }
async putFlat(item: Flat): Promise<Flat> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`FlatService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Flat>(`${serviceBaseUrl}/v1/flats/${id}`, item).toPromise()
}
async getFlatsByPremise(id: number): Promise<Flat[]> {
this.messageService.add(`FlatService: get data by Premise ${id}`);
return this.http.get<Flat[]>(`${serviceBaseUrl}/v1/flats/premise/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -142,6 +189,15 @@ export class OverheadAdvanceService {
return this.http.post<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advances`, item).toPromise() return this.http.post<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advances`, item).toPromise()
} }
async putOverheadAdvance(item: OverheadAdvance): Promise<OverheadAdvance> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`OverheadAdvanceService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advances/${id}`, item).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -164,6 +220,19 @@ export class OverheadAdvanceFlatMappingService {
return this.http.post<OverheadAdvanceFlatMapping>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings`, item).toPromise() return this.http.post<OverheadAdvanceFlatMapping>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings`, item).toPromise()
} }
async getOverheadAdvanceFlatMappingsByOverheadAdvance(id: number): Promise<OverheadAdvanceFlatMapping[]> {
this.messageService.add(`OverheadAdvanceFlatMappingService: get data by OverheadAdvance ${id}`);
return this.http.get<OverheadAdvanceFlatMapping[]>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings/overhead_advance/${id}`).toPromise()
}
async getOverheadAdvanceFlatMappingsByFlat(id: number): Promise<OverheadAdvanceFlatMapping[]> {
this.messageService.add(`OverheadAdvanceFlatMappingService: get data by Flat ${id}`);
return this.http.get<OverheadAdvanceFlatMapping[]>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings/flat/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -186,6 +255,20 @@ export class ParkingService {
return this.http.post<Parking>(`${serviceBaseUrl}/v1/parkings`, item).toPromise() return this.http.post<Parking>(`${serviceBaseUrl}/v1/parkings`, item).toPromise()
} }
async putParking(item: Parking): Promise<Parking> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`ParkingService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Parking>(`${serviceBaseUrl}/v1/parkings/${id}`, item).toPromise()
}
async getParkingsByPremise(id: number): Promise<Parking[]> {
this.messageService.add(`ParkingService: get data by Premise ${id}`);
return this.http.get<Parking[]>(`${serviceBaseUrl}/v1/parkings/premise/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -208,6 +291,20 @@ export class CommercialPremiseService {
return this.http.post<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premises`, item).toPromise() return this.http.post<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premises`, item).toPromise()
} }
async putCommercialPremise(item: CommercialPremise): Promise<CommercialPremise> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`CommercialPremiseService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premises/${id}`, item).toPromise()
}
async getCommercialPremisesByPremise(id: number): Promise<CommercialPremise[]> {
this.messageService.add(`CommercialPremiseService: get data by Premise ${id}`);
return this.http.get<CommercialPremise[]>(`${serviceBaseUrl}/v1/commercial_premises/premise/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -230,6 +327,35 @@ export class TenancyService {
return this.http.post<Tenancy>(`${serviceBaseUrl}/v1/tenancys`, item).toPromise() return this.http.post<Tenancy>(`${serviceBaseUrl}/v1/tenancys`, item).toPromise()
} }
async putTenancy(item: Tenancy): Promise<Tenancy> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`TenancyService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Tenancy>(`${serviceBaseUrl}/v1/tenancys/${id}`, item).toPromise()
}
async getTenancysByTenant(id: number): Promise<Tenancy[]> {
this.messageService.add(`TenancyService: get data by Tenant ${id}`);
return this.http.get<Tenancy[]>(`${serviceBaseUrl}/v1/tenancys/tenant/${id}`).toPromise()
}
async getTenancysByFlat(id: number): Promise<Tenancy[]> {
this.messageService.add(`TenancyService: get data by Flat ${id}`);
return this.http.get<Tenancy[]>(`${serviceBaseUrl}/v1/tenancys/flat/${id}`).toPromise()
}
async getTenancysByParking(id: number): Promise<Tenancy[]> {
this.messageService.add(`TenancyService: get data by Parking ${id}`);
return this.http.get<Tenancy[]>(`${serviceBaseUrl}/v1/tenancys/parking/${id}`).toPromise()
}
async getTenancysByCommercialPremise(id: number): Promise<Tenancy[]> {
this.messageService.add(`TenancyService: get data by CommercialPremise ${id}`);
return this.http.get<Tenancy[]>(`${serviceBaseUrl}/v1/tenancys/commercial_premise/${id}`).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -252,6 +378,15 @@ export class FeeService {
return this.http.post<Fee>(`${serviceBaseUrl}/v1/fees`, item).toPromise() return this.http.post<Fee>(`${serviceBaseUrl}/v1/fees`, item).toPromise()
} }
async putFee(item: Fee): Promise<Fee> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`FeeService: put data for ${itemStr}`)
let id: number = item["id"]
return this.http.put<Fee>(`${serviceBaseUrl}/v1/fees/${id}`, item).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -274,6 +409,44 @@ export class TenancyFeeMappingService {
return this.http.post<TenancyFeeMapping>(`${serviceBaseUrl}/v1/tenancy_fee_mappings`, item).toPromise() return this.http.post<TenancyFeeMapping>(`${serviceBaseUrl}/v1/tenancy_fee_mappings`, item).toPromise()
} }
async getTenancyFeeMappingsByTenancy(id: number): Promise<TenancyFeeMapping[]> {
this.messageService.add(`TenancyFeeMappingService: get data by Tenancy ${id}`);
return this.http.get<TenancyFeeMapping[]>(`${serviceBaseUrl}/v1/tenancy_fee_mappings/tenancy/${id}`).toPromise()
}
async getTenancyFeeMappingsByFee(id: number): Promise<TenancyFeeMapping[]> {
this.messageService.add(`TenancyFeeMappingService: get data by Fee ${id}`);
return this.http.get<TenancyFeeMapping[]>(`${serviceBaseUrl}/v1/tenancy_fee_mappings/fee/${id}`).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class AccountEntryCategoryService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getAccountEntryCategorys(): Promise<AccountEntryCategory[]> {
this.messageService.add(`AccountEntryCategoryService: get data`);
return this.http.get<AccountEntryCategory[]>(`${serviceBaseUrl}/v1/account_entry_categorys`).toPromise()
}
async getAccountEntryCategory(id: number): Promise<AccountEntryCategory> {
this.messageService.add(`AccountEntryCategoryService: get data for ${id}`);
return this.http.get<AccountEntryCategory>(`${serviceBaseUrl}/v1/account_entry_categorys/${id}`).toPromise()
}
async postAccountEntryCategory(item: AccountEntryCategory): Promise<AccountEntryCategory> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`AccountEntryCategoryService: post data for ${itemStr}`);
return this.http.post<AccountEntryCategory>(`${serviceBaseUrl}/v1/account_entry_categorys`, item).toPromise()
}
} }
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -296,6 +469,49 @@ export class AccountEntryService {
return this.http.post<AccountEntry>(`${serviceBaseUrl}/v1/account_entrys`, item).toPromise() return this.http.post<AccountEntry>(`${serviceBaseUrl}/v1/account_entrys`, item).toPromise()
} }
async getAccountEntrysByAccount(id: number): Promise<AccountEntry[]> {
this.messageService.add(`AccountEntryService: get data by Account ${id}`);
return this.http.get<AccountEntry[]>(`${serviceBaseUrl}/v1/account_entrys/account/${id}`).toPromise()
}
async getAccountEntrysByAccountEntryCategory(id: number): Promise<AccountEntry[]> {
this.messageService.add(`AccountEntryService: get data by AccountEntryCategory ${id}`);
return this.http.get<AccountEntry[]>(`${serviceBaseUrl}/v1/account_entrys/account_entry_category/${id}`).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class NoteService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getNotes(): Promise<Note[]> {
this.messageService.add(`NoteService: get data`);
return this.http.get<Note[]>(`${serviceBaseUrl}/v1/notes`).toPromise()
}
async getNote(id: number): Promise<Note> {
this.messageService.add(`NoteService: get data for ${id}`);
return this.http.get<Note>(`${serviceBaseUrl}/v1/notes/${id}`).toPromise()
}
async postNote(item: Note): Promise<Note> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`NoteService: post data for ${itemStr}`);
return this.http.post<Note>(`${serviceBaseUrl}/v1/notes`, item).toPromise()
}
async getNotesByTenant(id: number): Promise<Note[]> {
this.messageService.add(`NoteService: get data by Tenant ${id}`);
return this.http.get<Note[]>(`${serviceBaseUrl}/v1/notes/tenant/${id}`).toPromise()
}
} }

View File

@ -2,8 +2,7 @@ $GENERATED_TS_COMMENT
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs'; import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MessageService } from './message.service'; import { MessageService } from './message.service';
import { serviceBaseUrl } from './config'; import { serviceBaseUrl } from './config';
@ -42,6 +41,26 @@ export class ${JsNameConverter($table.name)}Service {
return this.http.post<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}s`, item).toPromise() return this.http.post<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}s`, item).toPromise()
} }
#if (('immutable' not in $table) or (not $table.immutable))
async put${JsNameConverter($table.name)}(item: ${JsNameConverter($table.name)}): Promise<${JsNameConverter($table.name)}> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`${JsNameConverter($table.name)}Service: put data for \${itemStr}`)
let id: number = item["id"]
return this.http.put<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}s/\${id}`, item).toPromise()
}
#end if
#for $column in $table.columns
#if (('foreignkey' in $column) and $column.foreignkey)
async get${JsNameConverter($table.name)}sBy${JsNameConverter($column.name)}(id: number): Promise<${JsNameConverter($table.name)}[]> {
this.messageService.add(`${JsNameConverter($table.name)}Service: get data by ${JsNameConverter($column.name)} \${id}`);
return this.http.get<${JsNameConverter($table.name)}[]>(`\${serviceBaseUrl}/v1/${table.name}s/${column.name}/\${id}`).toPromise()
}
#end if
#end for
} }
#end for #end for

View File

@ -10,6 +10,10 @@ export interface Account {
id: number id: number
description: string description: string
} }
export const NULL_Account: Account = {
id: 0
,description: ''
}
export interface Tenant { export interface Tenant {
id: number id: number
@ -26,6 +30,21 @@ export interface Tenant {
iban: string iban: string
account: number account: number
} }
export const NULL_Tenant: Tenant = {
id: 0
,salutation: ''
,firstname: ''
,lastname: ''
,address1: ''
,address2: ''
,address3: ''
,zip: ''
,city: ''
,phone1: ''
,phone2: ''
,iban: ''
,account: undefined
}
export interface Premise { export interface Premise {
id: number id: number
@ -34,6 +53,13 @@ export interface Premise {
zip: string zip: string
city: string city: string
} }
export const NULL_Premise: Premise = {
id: 0
,description: ''
,street: ''
,zip: ''
,city: ''
}
export interface Flat { export interface Flat {
id: number id: number
@ -42,6 +68,13 @@ export interface Flat {
area: number area: number
flat_no: number flat_no: number
} }
export const NULL_Flat: Flat = {
id: 0
,description: ''
,premise: undefined
,area: undefined
,flat_no: undefined
}
export interface OverheadAdvance { export interface OverheadAdvance {
id: number id: number
@ -50,24 +83,46 @@ export interface OverheadAdvance {
startdate: string startdate: string
enddate: string enddate: string
} }
export const NULL_OverheadAdvance: OverheadAdvance = {
id: 0
,description: ''
,amount: undefined
,startdate: ''
,enddate: ''
}
export interface OverheadAdvanceFlatMapping { export interface OverheadAdvanceFlatMapping {
id: number id: number
overhead_advance: number overhead_advance: number
flat: number flat: number
} }
export const NULL_OverheadAdvanceFlatMapping: OverheadAdvanceFlatMapping = {
id: 0
,overhead_advance: undefined
,flat: undefined
}
export interface Parking { export interface Parking {
id: number id: number
description: string description: string
premise: number premise: number
} }
export const NULL_Parking: Parking = {
id: 0
,description: ''
,premise: undefined
}
export interface CommercialPremise { export interface CommercialPremise {
id: number id: number
description: string description: string
premise: number premise: number
} }
export const NULL_CommercialPremise: CommercialPremise = {
id: 0
,description: ''
,premise: undefined
}
export interface Tenancy { export interface Tenancy {
id: number id: number
@ -79,6 +134,16 @@ export interface Tenancy {
startdate: string startdate: string
enddate: string enddate: string
} }
export const NULL_Tenancy: Tenancy = {
id: 0
,description: ''
,tenant: undefined
,flat: undefined
,parking: undefined
,commercial_premise: undefined
,startdate: ''
,enddate: ''
}
export interface Fee { export interface Fee {
id: number id: number
@ -88,12 +153,36 @@ export interface Fee {
startdate: string startdate: string
enddate: string enddate: string
} }
export const NULL_Fee: Fee = {
id: 0
,description: ''
,amount: undefined
,fee_type: ''
,startdate: ''
,enddate: ''
}
export interface TenancyFeeMapping { export interface TenancyFeeMapping {
id: number id: number
tenancy: number tenancy: number
fee: number fee: number
} }
export const NULL_TenancyFeeMapping: TenancyFeeMapping = {
id: 0
,tenancy: undefined
,fee: undefined
}
export interface AccountEntryCategory {
id: number
description: string
overhead_relevant: boolean
}
export const NULL_AccountEntryCategory: AccountEntryCategory = {
id: 0
,description: ''
,overhead_relevant: false
}
export interface AccountEntry { export interface AccountEntry {
id: number id: number
@ -101,6 +190,28 @@ export interface AccountEntry {
account: number account: number
created_at: string created_at: string
amount: number amount: number
account_entry_category: number
}
export const NULL_AccountEntry: AccountEntry = {
id: 0
,description: ''
,account: undefined
,created_at: ''
,amount: undefined
,account_entry_category: undefined
}
export interface Note {
id: number
created_at: string
tenant: number
note: string
}
export const NULL_Note: Note = {
id: 0
,created_at: ''
,tenant: undefined
,note: ''
} }

View File

@ -9,6 +9,19 @@ export interface $JsNameConverter($table.name) {
${column.name}: ${column.jstype} ${column.name}: ${column.jstype}
#end for #end for
} }
export const NULL_$JsNameConverter($table.name): $JsNameConverter($table.name) = {
id: 0
#for $column in $table.columns
,${column.name}: #slurp
#if $column.jstype == "string"
''
#else if $column.jstype == "number"
undefined
#else if $column.jstype == "boolean"
false
#end if
#end for
}
#end for #end for

View File

@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { MessageService } from './message.service';
import { serviceBaseUrl } from './config';
import { Fee, OverheadAdvance } from './data-objects';
import { Saldo } from './ext-data-objects';
@Injectable({ providedIn: 'root' })
export class ExtApiService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getOverheadAdvancesByFlat(id: number): Promise<OverheadAdvance[]> {
this.messageService.add(`ExtApiService: get overheadadvances by flat ${id}`);
return this.http.get<OverheadAdvance[]>(`${serviceBaseUrl}/v1/overhead_advances/flat/${id}`).toPromise()
}
async getFeeByTenancies(id: number): Promise<Fee[]> {
this.messageService.add(`ExtApiService: get fees by flat ${id}`);
return this.http.get<Fee[]>(`${serviceBaseUrl}/v1/fees/tenancy/${id}`).toPromise()
}
async getAccountSaldo(id: number): Promise<Saldo> {
this.messageService.add(`ExtApiService: get saldo for account ${id}`);
return this.http.get<Saldo>(`${serviceBaseUrl}/v1/account/saldo/${id}`).toPromise()
}
}

View File

@ -0,0 +1,5 @@
export interface Saldo {
saldo: number
}

View File

@ -0,0 +1,49 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{fee?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{fee?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveFee()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="fee.description"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Betrag (€)</mat-label>
<input matInput type="number" name="amount" [(ngModel)]="fee.amount" [readonly]="readonly"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Typ</mat-label>
<mat-select [(ngModel)]="fee.fee_type" name="fee_type">
<mat-option *ngFor="let p of fee_types" [value]="p">{{p}}</mat-option>
</mat-select>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Beginn</mat-label>
<input matInput name="startdate" [(ngModel)]="fee.startdate" [matDatepicker]="startdatepicker" [readonly]="readonly"/>
<mat-datepicker-toggle matSuffix [for]="startdatepicker" [disabled]="readonly"></mat-datepicker-toggle>
<mat-datepicker #startdatepicker></mat-datepicker>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Ende</mat-label>
<input matInput name="enddate" [(ngModel)]="fee.enddate" [matDatepicker]="enddatepicker"/>
<mat-datepicker-toggle matSuffix [for]="enddatepicker"></mat-datepicker-toggle>
<mat-datepicker #enddatepicker></mat-datepicker>
</mat-form-field>
</div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
</form>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FeeDetailsComponent } from './fee-details.component';
describe('FeeDetailsComponent', () => {
let component: FeeDetailsComponent;
let fixture: ComponentFixture<FeeDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FeeDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FeeDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,73 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { FeeService } from '../data-object-service';
import { NULL_Fee, Fee } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-fee-details',
templateUrl: './fee-details.component.html',
styleUrls: ['./fee-details.component.css']
})
export class FeeDetailsComponent implements OnInit {
@ViewChild('submitButton') submitButton: MatButton
fee: Fee = NULL_Fee
fee_types: string[] = [ "total", "per_area" ]
readonly: boolean
constructor(
private feeService: FeeService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getFee(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
this.readonly = false
if (id != 0) {
this.fee = await this.feeService.getFee(id)
this.readonly = true
}
this.messageService.add(`Fee is ${ JSON.stringify(this.fee, undefined, 4)}`)
} catch (err) {
this.messageService.add(`Error in getFee: ${ JSON.stringify(err, undefined, 4) }`)
}
}
async saveFee() {
try {
this.submitButton.disabled = true
this.messageService.add("saveFee")
this.messageService.add(JSON.stringify(this.fee, undefined, 4))
if (this.fee.enddate == null) {
this.fee.enddate = ''
}
if (this.fee.startdate == null) {
this.fee.startdate = ''
}
if (this.fee.id == 0) {
this.messageService.add("about to insert new fee")
this.fee = await this.feeService.postFee(this.fee)
this.messageService.add(`Successfully added fee with id ${this.fee.id}`)
} else {
this.messageService.add("about to update existing fee")
this.fee = await this.feeService.putFee(this.fee)
this.messageService.add(`Successfully changed fee with id ${this.fee.id}`)
}
this.router.navigate(['/fees'])
} finally {
this.submitButton.disabled = false
}
}
ngOnInit(): void {
this.getFee()
}
}

View File

@ -0,0 +1,8 @@
table {
width: 75%;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -0,0 +1,39 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
<span>Mietsätze</span>
<span class="spacer"></span>
<a mat-button routerLink="/fee">Neu anlegen</a>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div>
<table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Betrag</th>
<td mat-cell *matCellDef="let element">{{element.amount | number:'1.2-2'}} €</td>
</ng-container>
<ng-container matColumnDef="fee_type">
<th mat-header-cell *matHeaderCellDef>Typ</th>
<td mat-cell *matCellDef="let element">{{element.fee_type}}</td>
</ng-container>
<ng-container matColumnDef="startdate">
<th mat-header-cell *matHeaderCellDef>Beginn</th>
<td mat-cell *matCellDef="let element">{{element.startdate | date}}</td>
</ng-container>
<ng-container matColumnDef="enddate">
<th mat-header-cell *matHeaderCellDef>Ende</th>
<td mat-cell *matCellDef="let element">{{element.enddate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/fee/', row.id]"></tr>
</table>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FeeListComponent } from './fee-list.component';
describe('FeeListComponent', () => {
let component: FeeListComponent;
let fixture: ComponentFixture<FeeListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FeeListComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FeeListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,42 @@
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { FeeService } from '../data-object-service';
import { Fee } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-fee-list',
templateUrl: './fee-list.component.html',
styleUrls: ['./fee-list.component.css']
})
export class FeeListComponent implements OnInit {
fees: Fee[]
dataSource: MatTableDataSource<Fee>
displayedColumns: string[] = [ "description", "amount", "fee_type", "startdate", "enddate" ]
constructor(
private feeService: FeeService,
private messageService: MessageService
) { }
async getFees(): Promise<void> {
try {
this.messageService.add("Trying to load fees")
this.fees = await this.feeService.getFees()
this.messageService.add("Fees loaded")
this.dataSource = new MatTableDataSource<Fee>(this.fees)
} catch (err) {
this.messageService.add(`Error in getFees: ${ JSON.stringify(err, undefined, 4) }`)
}
}
ngOnInit(): void {
this.messageService.add("FeeListComponent.ngOnInit")
this.getFees()
}
}

View File

@ -0,0 +1,11 @@
table {
width: 75%;
}
.spacer {
flex: 1 1 auto;
}
#addoverheadfield {
margin-right: 15px;
}

View File

@ -25,11 +25,11 @@
</div><div> </div><div>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Fläche</mat-label> <mat-label>Fläche</mat-label>
<input matInput name="area" [(ngModel)]="flat.area"/> <input type="number" matInput name="area" [(ngModel)]="flat.area"/>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Wohnungsnummer</mat-label> <mat-label>Wohnungsnummer</mat-label>
<input matInput name="flat_no" [(ngModel)]="flat.flat_no"/> <input type="number" matInput name="flat_no" [(ngModel)]="flat.flat_no"/>
</mat-form-field> </mat-form-field>
</div> </div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button> <button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
@ -37,4 +37,48 @@
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
Betriebskostenzuordnungen
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div>
<table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Betrag</th>
<td mat-cell *matCellDef="let element">{{element.amount}} €</td>
</ng-container>
<ng-container matColumnDef="startdate">
<th mat-header-cell *matHeaderCellDef>Beginn</th>
<td mat-cell *matCellDef="let element">{{element.startdate}}</td>
</ng-container>
<ng-container matColumnDef="enddate">
<th mat-header-cell *matHeaderCellDef>Ende</th>
<td mat-cell *matCellDef="let element">{{element.enddate}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<div>
<form (ngSubmit)="addOverheadAdvance()">
<mat-form-field appearance="standard" id="addoverheadfield">
<mat-label>Betriebskostensatz</mat-label>
<mat-select #mapSelect [(ngModel)]="selectedOverheadAdvance" name="overheadadvance">
<mat-option *ngFor="let p of allOverheadAdvances" [value]="p.id">{{p.description}} {{p.startdate}}</mat-option>
</mat-select>
</mat-form-field>
<button #mapButton type="submit" mat-raised-button color="primary">Zuordnen</button>
</form>
</div>
</mat-card-content>
</mat-card>
</section> </section>

View File

@ -1,8 +1,11 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { MatSelect } from '@angular/material/select';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { FlatService, PremiseService } from '../data-object-service'; import { FlatService, OverheadAdvanceFlatMappingService, OverheadAdvanceService, PremiseService } from '../data-object-service';
import { Flat, Premise } from '../data-objects'; import { Flat, NULL_Flat, NULL_Premise, OverheadAdvance, OverheadAdvanceFlatMapping, Premise } from '../data-objects';
import { ExtApiService } from '../ext-data-object-service';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';
@Component({ @Component({
@ -12,29 +15,27 @@ import { MessageService } from '../message.service';
}) })
export class FlatDetailsComponent implements OnInit { export class FlatDetailsComponent implements OnInit {
flat: Flat = { flat: Flat = NULL_Flat
id: 0,
description: '',
premise: 0,
area: 0.0,
flat_no: 0
}
premise: Premise = { premise: Premise = NULL_Premise
id: 0,
description: '',
street: '',
zip: '',
city: ''
}
premises: Premise[] premises: Premise[]
mappedOverheadAdvances: OverheadAdvance[]
allOverheadAdvances: OverheadAdvance[]
selectedOverheadAdvance: number
dataSource: MatTableDataSource<OverheadAdvance>
displayedColumns: string[] = [ "description", "amount", "startdate", "enddate" ]
@ViewChild('submitButton') submitButton: MatButton @ViewChild('submitButton') submitButton: MatButton
@ViewChild('mapButton') mapButton: MatButton
@ViewChild('mapSelect') mapSelect: MatSelect
constructor( constructor(
private flatService: FlatService, private flatService: FlatService,
private premiseService: PremiseService, private premiseService: PremiseService,
private extApiService: ExtApiService,
private overheadAdvanceService: OverheadAdvanceService,
private overheadAdvanceFlatMappingService: OverheadAdvanceFlatMappingService,
private messageService: MessageService, private messageService: MessageService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router private router: Router
@ -43,11 +44,20 @@ export class FlatDetailsComponent implements OnInit {
async getFlat(): Promise<void> { async getFlat(): Promise<void> {
try { try {
const id = +this.route.snapshot.paramMap.get('id') const id = +this.route.snapshot.paramMap.get('id')
this.messageService.add(`getFlat ${id}`)
if (id != 0) { if (id != 0) {
this.flat = await this.flatService.getFlat(id) this.flat = await this.flatService.getFlat(id)
this.premise = await this.premiseService.getPremise(this.flat.premise) this.premise = await this.premiseService.getPremise(this.flat.premise)
this.mappedOverheadAdvances = await this.extApiService.getOverheadAdvancesByFlat(this.flat.id)
this.dataSource = new MatTableDataSource<OverheadAdvance>(this.mappedOverheadAdvances)
} else {
this.flat = NULL_Flat
this.premise = NULL_Premise
this.mappedOverheadAdvances = undefined
this.dataSource = undefined
} }
} catch (err) { } catch (err) {
throw err
this.messageService.add(JSON.stringify(err, undefined, 4)) this.messageService.add(JSON.stringify(err, undefined, 4))
} }
} }
@ -62,21 +72,59 @@ export class FlatDetailsComponent implements OnInit {
} }
} }
async saveFlat() { async getOverheadAdvances(): Promise<void> {
this.submitButton.disabled = true try {
this.messageService.add(`saveFlat: ${ JSON.stringify(this.flat, undefined, 4) }`) this.messageService.add("Trying to load overheadadvances")
if (this.flat.id == 0) { this.allOverheadAdvances = await this.overheadAdvanceService.getOverheadAdvances()
this.messageService.add("about to insert new flat") this.messageService.add("overheadadvances loaded")
this.flat = await this.flatService.postFlat(this.flat) } catch (err) {
this.messageService.add(`Successfully added flat with id ${this.flat.id}`) this.messageService.add(JSON.stringify(err, undefined, 4))
} else { }
this.messageService.add("about to update existing flat") }
async saveFlat() {
try {
this.submitButton.disabled = true
this.messageService.add(`saveFlat: ${ JSON.stringify(this.flat, undefined, 4) }`)
if (this.flat.id == 0) {
this.messageService.add("about to insert new flat")
this.flat = await this.flatService.postFlat(this.flat)
this.messageService.add(`Successfully added flat with id ${this.flat.id}`)
} else {
this.messageService.add("about to update existing flat")
this.flat = await this.flatService.putFlat(this.flat)
this.messageService.add(`Successfully changed flat with id ${this.flat.id}`)
}
this.router.navigate(['/flats'])
} finally {
this.submitButton.disabled = false
}
}
async addOverheadAdvance() {
try {
this.mapButton.disabled = true
this.messageService.add(`addOverheadAdvance: ${ JSON.stringify(this.selectedOverheadAdvance, undefined, 4) }`)
let newMapping: OverheadAdvanceFlatMapping = {
'flat': this.flat.id,
'overhead_advance': this.selectedOverheadAdvance,
'id': 0
}
newMapping = await this.overheadAdvanceFlatMappingService.postOverheadAdvanceFlatMapping(newMapping)
this.messageService.add(`New overheadadvance flat mapping created: ${newMapping.id}`)
this.selectedOverheadAdvance = undefined
this.getFlat()
} finally {
this.mapButton.disabled = false
} }
this.router.navigate(['/flats'])
} }
ngOnInit(): void { ngOnInit(): void {
this.messageService.add("FlatDetailsComponent.ngOnInit")
this.getPremises() this.getPremises()
this.getOverheadAdvances()
this.getFlat() this.getFlat()
} }

View File

@ -1,11 +1,24 @@
<section class="mat-typography"> <section class="mat-typography">
<mat-card class="defaultCard" *ngIf="messageService.messages.length"> <mat-card class="defaultCard">
<mat-card-header> <mat-card-header>
<mat-card-title>Messages</mat-card-title> <mat-card-title>Messages</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<button (click)="messageService.clear()">clear</button> <mat-accordion>
<mat-expansion-panel (opened)="collapse = true"
(closed)="collapse = false">
<mat-expansion-panel-header>
<mat-panel-title *ngIf="!collapse">
Übersicht
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
<button (click)="messageService.clear()">clear</button>
<div *ngFor='let message of messageService.messages'> {{message}} </div> <div *ngFor='let message of messageService.messages'> {{message}} </div>
</mat-expansion-panel>
</mat-accordion>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</section> </section>

View File

@ -8,6 +8,8 @@ import { MessageService } from '../message.service';
}) })
export class MessagesComponent implements OnInit { export class MessagesComponent implements OnInit {
collapse: boolean = false
constructor(public messageService: MessageService) { } constructor(public messageService: MessageService) { }
ngOnInit(): void { ngOnInit(): void {

View File

@ -3,6 +3,9 @@
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
Meine Büros Meine Büros
<span>Meine Büros</span>
<span class="spacer"></span>
<a mat-button routerLink="/commercialunit">Neu anlegen</a>
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
@ -10,14 +13,14 @@
<table mat-table [dataSource]="dataSource" #zftable> <table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="description"> <ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th> <th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td> <td mat-cell *matCellDef="let element">{{element.commercialPremise.description}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="premise"> <ng-container matColumnDef="premise">
<th mat-header-cell *matHeaderCellDef>Haus</th> <th mat-header-cell *matHeaderCellDef>Haus</th>
<td mat-cell *matCellDef="let element">{{element.premise}}</td> <td mat-cell *matCellDef="let element">{{element.premise.description}}</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/commercialunit/', row.id]"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/commercialunit/', row.commercialPremise.id]"></tr>
</table> </table>
</div> </div>
</mat-card-content> </mat-card-content>

View File

@ -1,9 +1,14 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';
import { CommercialPremiseService } from '../data-object-service'; import { CommercialPremiseService, PremiseService } from '../data-object-service';
import { CommercialPremise } from '../data-objects'; import { CommercialPremise, Premise } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table' import { MatTableDataSource } from '@angular/material/table'
interface DN_CommercialPremise {
commercialPremise: CommercialPremise
premise: Premise
}
@Component({ @Component({
selector: 'app-my-commercial-units', selector: 'app-my-commercial-units',
templateUrl: './my-commercial-units.component.html', templateUrl: './my-commercial-units.component.html',
@ -12,18 +17,40 @@ import { MatTableDataSource } from '@angular/material/table'
export class MyCommercialUnitsComponent implements OnInit { export class MyCommercialUnitsComponent implements OnInit {
commercialPremises: CommercialPremise[] commercialPremises: CommercialPremise[]
dataSource: MatTableDataSource<CommercialPremise> premises: Premise[]
dnCommercialPremises: DN_CommercialPremise[] = []
dataSource: MatTableDataSource<DN_CommercialPremise>
displayedColumns: string[] = ["description", "premise"] displayedColumns: string[] = ["description", "premise"]
constructor(private commercialPremiseService: CommercialPremiseService, private messageService: MessageService) { } constructor(
private commercialPremiseService: CommercialPremiseService,
private premiseService: PremiseService,
private messageService: MessageService
) { }
async getCommercialPremises(): Promise<void> { async getCommercialPremises(): Promise<void> {
try { try {
this.messageService.add("Trying to load commercialPremises") this.messageService.add("Trying to load commercialPremises")
this.commercialPremises = await this.commercialPremiseService.getCommercialPremises() this.commercialPremises = await this.commercialPremiseService.getCommercialPremises()
this.messageService.add("CommercialPremises loaded") this.messageService.add("CommercialPremises loaded")
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add(`Premises loaded: ${ JSON.stringify(this.premises, undefined, 4) }`)
this.dataSource = new MatTableDataSource<CommercialPremise>(this.commercialPremises) const premisesDict = new Map<number, Premise>()
for (let p of this.premises) {
premisesDict.set(p.id, p)
}
for (let f of this.commercialPremises) {
this.dnCommercialPremises.push({
commercialPremise: f,
premise: premisesDict.get(f.premise)
})
}
this.dataSource = new MatTableDataSource<DN_CommercialPremise>(this.dnCommercialPremises)
} catch (err) { } catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4)) this.messageService.add(JSON.stringify(err, undefined, 4))
} }

View File

@ -1,3 +0,0 @@
table {
width: 75%;
}

View File

@ -20,14 +20,14 @@
</ng-container> </ng-container>
<ng-container matColumnDef="area"> <ng-container matColumnDef="area">
<th mat-header-cell *matHeaderCellDef>Wohnfläche</th> <th mat-header-cell *matHeaderCellDef>Wohnfläche</th>
<td mat-cell *matCellDef="let element">{{element.flat.area}}</td> <td mat-cell *matCellDef="let element">{{element.flat.area | number:'1.2-2'}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="flat_no"> <ng-container matColumnDef="flat_no">
<th mat-header-cell *matHeaderCellDef>Wohnungsnummer</th> <th mat-header-cell *matHeaderCellDef>Wohnungsnummer</th>
<td mat-cell *matCellDef="let element">{{element.flat.flat_no}}</td> <td mat-cell *matCellDef="let element">{{element.flat.flat_no}}</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/flat/', row.id]"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/flat/', row.flat.id]"></tr>
</table> </table>
</div> </div>
</mat-card-content> </mat-card-content>

View File

@ -17,7 +17,7 @@ interface DN_Flat {
export class MyFlatsComponent implements OnInit { export class MyFlatsComponent implements OnInit {
flats: Flat[] flats: Flat[]
premises: Premise[] premises: Premise[]
dnFlats: DN_Flat[] dnFlats: DN_Flat[] = []
dataSource: MatTableDataSource<DN_Flat> dataSource: MatTableDataSource<DN_Flat>
displayedColumns: string[] = ["description", "premise", "area", "flat_no"] displayedColumns: string[] = ["description", "premise", "area", "flat_no"]
@ -39,20 +39,18 @@ export class MyFlatsComponent implements OnInit {
const premisesDict = new Map<number, Premise>() const premisesDict = new Map<number, Premise>()
for (let p of this.premises) { for (let p of this.premises) {
this.messageService.add(`p2pd: ${p.id}`)
premisesDict.set(p.id, p) premisesDict.set(p.id, p)
} }
this.messageService.add(`premisesDict: ${ JSON.stringify(premisesDict, undefined, 4) }`)
for (let f of this.flats) { for (let f of this.flats) {
this.dnFlats.push({ this.dnFlats.push({
flat: f, flat: f,
premise: premisesDict.get(f.premise) premise: premisesDict.get(f.premise)
}) })
} }
this.messageService.add(`dnFlats: { JSON.stringify(this.dnFlats, undefined, 4) }`)
this.dataSource = new MatTableDataSource<DN_Flat>(this.dnFlats) this.dataSource = new MatTableDataSource<DN_Flat>(this.dnFlats)
} catch (err) { } catch (err) {
// throw err
this.messageService.add(`Error in getFlats: ${ JSON.stringify(err, undefined, 4) }`) this.messageService.add(`Error in getFlats: ${ JSON.stringify(err, undefined, 4) }`)
} }
} }

View File

@ -1,3 +0,0 @@
table {
width: 75%;
}

View File

@ -3,6 +3,9 @@
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
Meine Garagen Meine Garagen
<span>Meine Garagen</span>
<span class="spacer"></span>
<a mat-button routerLink="/parking">Neu anlegen</a>
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
@ -10,14 +13,14 @@
<table mat-table [dataSource]="dataSource" #zftable> <table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="description"> <ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th> <th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td> <td mat-cell *matCellDef="let element">{{element.parking.description}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="premise"> <ng-container matColumnDef="premise">
<th mat-header-cell *matHeaderCellDef>Haus</th> <th mat-header-cell *matHeaderCellDef>Haus</th>
<td mat-cell *matCellDef="let element">{{element.premise}}</td> <td mat-cell *matCellDef="let element">{{element.premise.description}}</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/parking/', row.id]"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/parking/', row.parking.id]"></tr>
</table> </table>
</div> </div>
</mat-card-content> </mat-card-content>

View File

@ -1,29 +1,54 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';
import { ParkingService } from '../data-object-service'; import { ParkingService, PremiseService } from '../data-object-service';
import { Parking } from '../data-objects'; import { Parking, Premise } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table' import { MatTableDataSource } from '@angular/material/table'
interface DN_Parking {
parking: Parking
premise: Premise
}
@Component({ @Component({
selector: 'app-my-parkings', selector: 'app-my-parkings',
templateUrl: './my-parkings.component.html', templateUrl: './my-parkings.component.html',
styleUrls: ['./my-parkings.component.css'] styleUrls: ['./my-parkings.component.css']
}) })
export class MyParkingsComponent implements OnInit { export class MyParkingsComponent implements OnInit {
parkings: Parking[] parkings: Parking[]
dataSource: MatTableDataSource<Parking> premises: Premise[]
dnParkings: DN_Parking[] = []
dataSource: MatTableDataSource<DN_Parking>
displayedColumns: string[] = ["description", "premise"] displayedColumns: string[] = ["description", "premise"]
constructor(private parkingService: ParkingService, private messageService: MessageService) { } constructor(
private parkingService: ParkingService,
private premiseService: PremiseService,
private messageService: MessageService
) { }
async getParkings(): Promise<void> { async getParkings(): Promise<void> {
try { try {
this.messageService.add("Trying to load parkings") this.messageService.add("Trying to load parkings")
this.parkings = await this.parkingService.getParkings() this.parkings = await this.parkingService.getParkings()
this.messageService.add("Parkings loaded") this.messageService.add("Parkings loaded")
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add(`Premises loaded: ${ JSON.stringify(this.premises, undefined, 4) }`)
this.dataSource = new MatTableDataSource<Parking>(this.parkings) const premisesDict = new Map<number, Premise>()
for (let p of this.premises) {
premisesDict.set(p.id, p)
}
for (let p of this.parkings) {
this.dnParkings.push({
parking: p,
premise: premisesDict.get(p.premise)
})
}
this.dataSource = new MatTableDataSource<DN_Parking>(this.dnParkings)
} catch (err) { } catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4)) this.messageService.add(JSON.stringify(err, undefined, 4))
} }

View File

@ -1,8 +0,0 @@
table {
width: 75%;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -1,8 +0,0 @@
table {
width: 75%;
}
.spacer {
flex: 1 1 auto;
}

View File

@ -16,10 +16,6 @@
z-index: 1; z-index: 1;
} }
.spacer {
flex: 1 1 auto;
}
.gittagversion { .gittagversion {
font-size: x-small; font-size: x-small;
margin-right: 5em; margin-right: 5em;

View File

@ -5,11 +5,16 @@
[opened]="(isHandset$ | async) === false"> [opened]="(isHandset$ | async) === false">
<mat-toolbar>Menu</mat-toolbar> <mat-toolbar>Menu</mat-toolbar>
<mat-nav-list> <mat-nav-list>
<a mat-list-item href="/premises">Meine Häuser</a> <a mat-list-item href="/tenants">Meine Mieter/innen</a>
</mat-nav-list><mat-divider></mat-divider><mat-nav-list>
<a mat-list-item href="/flats">Meine Wohnungen</a> <a mat-list-item href="/flats">Meine Wohnungen</a>
<a mat-list-item href="/parkings">Meine Garagen</a> <a mat-list-item href="/parkings">Meine Garagen</a>
<a mat-list-item href="/commercialunits">Meine Büros</a> <a mat-list-item href="/commercialunits">Meine Büros</a>
<a mat-list-item href="/tenants">Meine Mieter/innen</a> </mat-nav-list><mat-divider></mat-divider><mat-nav-list>
<a mat-list-item href="/overheadadvances">Betriebskostensätze</a>
<a mat-list-item href="/fees">Mietsätze</a>
</mat-nav-list><mat-divider></mat-divider><mat-nav-list>
<a mat-list-item href="/premises">Meine Häuser</a>
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>
<mat-sidenav-content> <mat-sidenav-content>
@ -25,6 +30,7 @@
<span>Nober Grundbesitz GbR Hausverwaltung</span> <span>Nober Grundbesitz GbR Hausverwaltung</span>
<span class="spacer"></span> <span class="spacer"></span>
<span class="gittagversion">GITTAGVERSION</span> <span class="gittagversion">GITTAGVERSION</span>
<span class="gittagversion" *ngIf="authenticated">Läuft aus in {{expiryTime | async }} Sekunden</span>
<a *ngIf="!authenticated" mat-button routerLink="/login">Login</a> <a *ngIf="!authenticated" mat-button routerLink="/login">Login</a>
<a *ngIf="authenticated" mat-button routerLink="/logout">Logout</a> <a *ngIf="authenticated" mat-button routerLink="/logout">Logout</a>
</mat-toolbar> </mat-toolbar>

View File

@ -5,6 +5,8 @@ import { map, shareReplay } from 'rxjs/operators';
import { TokenService } from '../token.service'; import { TokenService } from '../token.service';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
@Component({ @Component({
selector: 'app-navigation', selector: 'app-navigation',
templateUrl: './navigation.component.html', templateUrl: './navigation.component.html',
@ -31,6 +33,10 @@ export class NavigationComponent {
}) })
} }
get expiryTime(): Observable<number> {
return this.tokenService.expiryTime
}
ngOnInit() { ngOnInit() {
this.authenticated = this.tokenService.checkAuthenticated() this.authenticated = this.tokenService.checkAuthenticated()
} }

View File

@ -0,0 +1,32 @@
#addEntryfield {
margin-right: 15px;
}
#divider {
margin-top: 20px;
}
#firstblock {
margin-bottom: 20px;
}
#secondblock {
margin-top: 20px;
}
.rightaligned {
justify-self: right;
}
.large {
font-size: large;
}
.notearea {
width: 75%;
}
.topalign {
vertical-align: top;
padding-right: 10px;
}

View File

@ -0,0 +1,53 @@
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
Notizen
</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-accordion>
<mat-expansion-panel (opened)="collapse = true"
(closed)="collapse = false">
<mat-expansion-panel-header>
<mat-panel-title *ngIf="!collapse">
Übersicht
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
<div id="firstBlock">
<form (ngSubmit)="addNote()">
<div>
<mat-form-field appearance="outline" class="notearea">
<mat-label>Text</mat-label>
<textarea matInput
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="5"
cdkAutosizeMaxRows="10"
name="note" [(ngModel)]="newNote.note"></textarea>
</mat-form-field>
</div><div>
<button #addNoteButton type="submit" mat-raised-button color="primary">Speichern</button>
</div>
</form>
</div>
<div id="secondBlock">
<table mat-table [dataSource]="notesDataSource" #zftable>
<ng-container matColumnDef="createdAt">
<th mat-header-cell *matHeaderCellDef>Datum</th>
<td mat-cell *matCellDef="let element" class="topalign">{{element.created_at | date}}</td>
</ng-container>
<ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef>Notiz</th>
<td mat-cell *matCellDef="let element" class="topalign">{{element.note}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="notesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: notesDisplayedColumns;"></tr>
</table>
</div>
</mat-expansion-panel>
</mat-accordion>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoteComponent } from './note.component';
describe('NoteComponent', () => {
let component: NoteComponent;
let fixture: ComponentFixture<NoteComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NoteComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NoteComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,77 @@
import { Component, Input, OnInit, OnChanges, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatTableDataSource } from '@angular/material/table';
import { NoteService } from '../data-object-service';
import { Note, NULL_Note } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-note',
templateUrl: './note.component.html',
styleUrls: ['./note.component.css']
})
export class NoteComponent implements OnInit {
@Input() selectedTenantId: number
@ViewChild('addNoteButton') addNoteButton: MatButton
collapse: boolean = false
notes: Note[]
notesDataSource: MatTableDataSource<Note>
notesDisplayedColumns: string[] = [ "createdAt", "note" ]
newNote : Note = NULL_Note
constructor(
private noteService: NoteService,
private messageService: MessageService
) { }
async getNotes(): Promise<void> {
try {
if (this.selectedTenantId) {
this.messageService.add(`Trying to load note for ${this.selectedTenantId}`)
this.notes = await this.noteService.getNotesByTenant(this.selectedTenantId)
this.notes.reverse()
this.messageService.add(`Notes: ${JSON.stringify(this.notes, undefined, 4)}`)
this.notesDataSource = new MatTableDataSource<Note>(this.notes)
}
} catch (err) {
this.messageService.add(`Error in getNotes: ${JSON.stringify(err, undefined, 4)}`)
}
}
async addNote(): Promise<void> {
try {
this.addNoteButton.disabled = true
this.newNote.tenant = this.selectedTenantId
this.newNote.created_at = new Date().toDateString()
this.messageService.add(`addNote: ${ JSON.stringify(this.newNote, undefined, 4) }`)
this.newNote = await this.noteService.postNote(this.newNote)
this.messageService.add(`New addNote created: ${this.newNote.id}`)
this.newNote = { 'tenant': this.selectedTenantId, 'note': '', 'created_at': '', 'id': 0 }
this.getNotes()
} catch (err) {
this.messageService.add(`Error in addNote: ${JSON.stringify(err, undefined, 4)}`)
} finally {
this.addNoteButton.disabled = false
}
}
ngOnInit(): void {
this.messageService.add(`NoteComponent.ngOnInit, account: ${this.selectedTenantId}`)
this.getNotes()
}
ngOnChanges(): void {
this.messageService.add(`NoteComponent.ngOnChanges, account: ${this.selectedTenantId}`)
this.getNotes()
}
}

View File

@ -0,0 +1,43 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{overheadAdvance?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{overheadAdvance?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveOverheadAdvance()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="overheadAdvance.description"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Betrag (€)</mat-label>
<input matInput type="number" name="amount" [(ngModel)]="overheadAdvance.amount" [readonly]="readonly"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Beginn</mat-label>
<input matInput name="startdate" [(ngModel)]="overheadAdvance.startdate" [matDatepicker]="startdatepicker" [readonly]="readonly"/>
<mat-datepicker-toggle matSuffix [for]="startdatepicker" [disabled]="readonly"></mat-datepicker-toggle>
<mat-datepicker #startdatepicker></mat-datepicker>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Ende</mat-label>
<input matInput name="enddate" [(ngModel)]="overheadAdvance.enddate" [matDatepicker]="enddatepicker"/>
<mat-datepicker-toggle matSuffix [for]="enddatepicker"></mat-datepicker-toggle>
<mat-datepicker #enddatepicker></mat-datepicker>
</mat-form-field>
</div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
</form>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverheadAdvanceDetailsComponent } from './overhead-advance-details.component';
describe('OverheadAdvanceDetailsComponent', () => {
let component: OverheadAdvanceDetailsComponent;
let fixture: ComponentFixture<OverheadAdvanceDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ OverheadAdvanceDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OverheadAdvanceDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,71 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { OverheadAdvanceService } from '../data-object-service';
import { NULL_OverheadAdvance, OverheadAdvance } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-overhead-advance-details',
templateUrl: './overhead-advance-details.component.html',
styleUrls: ['./overhead-advance-details.component.css']
})
export class OverheadAdvanceDetailsComponent implements OnInit {
@ViewChild('submitButton') submitButton: MatButton
overheadAdvance: OverheadAdvance = NULL_OverheadAdvance
readonly: boolean
constructor(
private overheadAdvanceService: OverheadAdvanceService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getOverheadAdvance(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
this.readonly = false
if (id != 0) {
this.overheadAdvance = await this.overheadAdvanceService.getOverheadAdvance(id)
this.readonly = true
}
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async saveOverheadAdvance() {
try {
this.submitButton.disabled = true
this.messageService.add("saveOverheadAdvance")
this.messageService.add(JSON.stringify(this.overheadAdvance, undefined, 4))
if (this.overheadAdvance.enddate == null) {
this.overheadAdvance.enddate = ''
}
if (this.overheadAdvance.startdate == null) {
this.overheadAdvance.startdate = ''
}
if (this.overheadAdvance.id == 0) {
this.messageService.add("about to insert new overheadAdvance")
this.overheadAdvance = await this.overheadAdvanceService.postOverheadAdvance(this.overheadAdvance)
this.messageService.add(`Successfully added overheadAdvance with id ${this.overheadAdvance.id}`)
} else {
this.messageService.add("about to update existing overheadAdvance")
this.overheadAdvance = await this.overheadAdvanceService.putOverheadAdvance(this.overheadAdvance)
this.messageService.add(`Successfully changed overheadAdvance with id ${this.overheadAdvance.id}`)
}
this.router.navigate(['/overheadadvances'])
} finally {
this.submitButton.disabled = false
}
}
ngOnInit(): void {
this.getOverheadAdvance()
}
}

View File

@ -0,0 +1,35 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
<span>Betriebskostensätze</span>
<span class="spacer"></span>
<a mat-button routerLink="/overheadadvance">Neu anlegen</a>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div>
<table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Betrag</th>
<td mat-cell *matCellDef="let element">{{element.amount | number:'1.2-2'}} €</td>
</ng-container>
<ng-container matColumnDef="startdate">
<th mat-header-cell *matHeaderCellDef>Beginn</th>
<td mat-cell *matCellDef="let element">{{element.startdate | date}}</td>
</ng-container>
<ng-container matColumnDef="enddate">
<th mat-header-cell *matHeaderCellDef>Ende</th>
<td mat-cell *matCellDef="let element">{{element.enddate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/overheadadvance/', row.id]"></tr>
</table>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverheadAdvanceListComponent } from './overhead-advance-list.component';
describe('OverheadAdvanceListComponent', () => {
let component: OverheadAdvanceListComponent;
let fixture: ComponentFixture<OverheadAdvanceListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ OverheadAdvanceListComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OverheadAdvanceListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,41 @@
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { OverheadAdvanceService } from '../data-object-service';
import { OverheadAdvance } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-overhead-advance-list',
templateUrl: './overhead-advance-list.component.html',
styleUrls: ['./overhead-advance-list.component.css']
})
export class OverheadAdvanceListComponent implements OnInit {
overheadAdvances: OverheadAdvance[]
dataSource: MatTableDataSource<OverheadAdvance>
displayedColumns: string[] = [ "description", "amount", "startdate", "enddate" ]
constructor(
private overheadAdvanceService: OverheadAdvanceService,
private messageService: MessageService
) { }
async getOverheadAdvances(): Promise<void> {
try {
this.messageService.add("Trying to load overheadAdvances")
this.overheadAdvances = await this.overheadAdvanceService.getOverheadAdvances()
this.messageService.add("OverheadAdvances loaded")
this.dataSource = new MatTableDataSource<OverheadAdvance>(this.overheadAdvances)
} catch (err) {
this.messageService.add(`Error in getOverheadAdvances: ${ JSON.stringify(err, undefined, 4) }`)
}
}
ngOnInit(): void {
this.messageService.add("OverheadAdvanceListComponent.ngOnInit")
this.getOverheadAdvances()
}
}

View File

@ -0,0 +1,31 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{parking?.description}} {{premise?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{parking?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveParking()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="parking.description"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Haus</mat-label>
<mat-select [(ngModel)]="parking.premise" name="premise">
<mat-option *ngFor="let p of premises" [value]="p.id">{{p.description}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
</form>
</div>
</mat-card-content>
</mat-card>
</section>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ParkingDetailsComponent } from './parking-details.component';
describe('ParkingDetailsComponent', () => {
let component: ParkingDetailsComponent;
let fixture: ComponentFixture<ParkingDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ParkingDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ParkingDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,79 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { ParkingService, PremiseService } from '../data-object-service';
import { NULL_Parking, NULL_Premise, Parking, Premise } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-parking-details',
templateUrl: './parking-details.component.html',
styleUrls: ['./parking-details.component.css']
})
export class ParkingDetailsComponent implements OnInit {
@ViewChild('submitButton') submitButton: MatButton
parking: Parking = NULL_Parking
premise: Premise = NULL_Premise
premises: Premise[]
constructor(
private parkingService: ParkingService,
private premiseService: PremiseService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getParking(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) {
this.parking = await this.parkingService.getParking(id)
this.premise = await this.premiseService.getPremise(this.parking.premise)
}
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async getPremises(): Promise<void> {
try {
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add("Premises loaded")
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async saveParking() {
try {
this.submitButton.disabled = true
this.messageService.add(`saveParking: ${ JSON.stringify(this.parking, undefined, 4) }`)
if (this.parking.id == 0) {
this.messageService.add("about to insert new parking")
this.parking = await this.parkingService.postParking(this.parking)
this.messageService.add(`Successfully added parking with id ${this.parking.id}`)
} else {
this.messageService.add("about to update existing parking")
this.parking = await this.parkingService.putParking(this.parking)
this.messageService.add(`Successfully changed parking with id ${this.parking.id}`)
}
this.router.navigate(['/parkings'])
} finally {
this.submitButton.disabled = false
}
}
ngOnInit(): void {
this.getPremises()
this.getParking()
}
}

View File

@ -41,18 +41,23 @@ export class PremiseDetailsComponent implements OnInit {
} }
async savePremise() { async savePremise() {
this.submitButton.disabled = true try {
this.messageService.add("savePremise") this.submitButton.disabled = true
this.messageService.add(JSON.stringify(this.premise, undefined, 4)) this.messageService.add("savePremise")
if (this.premise.id == 0) { this.messageService.add(JSON.stringify(this.premise, undefined, 4))
this.messageService.add("about to insert new premise") if (this.premise.id == 0) {
this.premise = await this.premiseService.postPremise(this.premise) this.messageService.add("about to insert new premise")
this.messageService.add(`Successfully added premises with id ${this.premise.id}`) this.premise = await this.premiseService.postPremise(this.premise)
} else { this.messageService.add(`Successfully added premises with id ${this.premise.id}`)
this.messageService.add("about to update existing premise") } else {
this.messageService.add("about to update existing premise")
this.premise = await this.premiseService.putPremise(this.premise)
this.messageService.add(`Successfully changed premises with id ${this.premise.id}`)
}
this.router.navigate(['/premises'])
} finally {
this.submitButton.disabled = false
} }
this.router.navigate(['/premises'])
// this.submitButton.disabled = false
} }
ngOnInit(): void { ngOnInit(): void {

View File

@ -0,0 +1,7 @@
#setenddatefield {
margin-right: 15px;
}
#addfeefield {
margin-right: 15px;
}

View File

@ -2,77 +2,212 @@
<mat-card class="defaultCard"> <mat-card class="defaultCard">
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
{{tenant?.firstname}} {{tenant?.lastname}} {{tenant?.firstname}} {{tenant?.lastname}}
</mat-card-title> </mat-card-title>
<mat-card-subtitle>
ID: {{tenant?.id}}
</mat-card-subtitle>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<div> <mat-accordion>
<form (ngSubmit)="saveTenant()"> <mat-expansion-panel (opened)="collapseTenantDetails = true"
(closed)="collapseTenantDetails = false">
<mat-expansion-panel-header>
<mat-panel-title *ngIf="!collapseTenantDetails">
Details
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
<div> <div>
<mat-form-field appearance="outline"> <form (ngSubmit)="saveTenant()">
<mat-label>Anrede</mat-label> <div>
<input matInput name="salutation" [(ngModel)]="tenant.salutation"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Anrede</mat-label>
<mat-form-field appearance="outline"> <input matInput name="salutation" [(ngModel)]="tenant.salutation"/>
<mat-label>Vorname</mat-label> </mat-form-field>
<input matInput name="firstname" [(ngModel)]="tenant.firstname"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Vorname</mat-label>
<mat-form-field appearance="outline"> <input matInput name="firstname" [(ngModel)]="tenant.firstname"/>
<mat-label>Nachname</mat-label> </mat-form-field>
<input matInput name="lastname" [(ngModel)]="tenant.lastname"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Nachname</mat-label>
</div><div> <input matInput name="lastname" [(ngModel)]="tenant.lastname"/>
<mat-form-field appearance="outline"> </mat-form-field>
<mat-label>Adresse 1 (Straße)</mat-label> </div><div>
<input matInput name="address" [(ngModel)]="tenant.address1"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Adresse 1 (Straße)</mat-label>
<mat-form-field appearance="outline"> <input matInput name="address" [(ngModel)]="tenant.address1"/>
<mat-label>Adresse 2</mat-label> </mat-form-field>
<input matInput name="address2" [(ngModel)]="tenant.address2"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Adresse 2</mat-label>
<mat-form-field appearance="outline"> <input matInput name="address2" [(ngModel)]="tenant.address2"/>
<mat-label>Adresse 3</mat-label> </mat-form-field>
<input matInput name="address3" [(ngModel)]="tenant.address3"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Adresse 3</mat-label>
</div><div> <input matInput name="address3" [(ngModel)]="tenant.address3"/>
<mat-form-field appearance="outline"> </mat-form-field>
<mat-label>PLZ</mat-label> </div><div>
<input matInput name="zip" [(ngModel)]="tenant.zip"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>PLZ</mat-label>
<mat-form-field appearance="outline"> <input matInput name="zip" [(ngModel)]="tenant.zip"/>
<mat-label>Ort</mat-label> </mat-form-field>
<input matInput name="city" [(ngModel)]="tenant.city"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Ort</mat-label>
</div><div> <input matInput name="city" [(ngModel)]="tenant.city"/>
<mat-form-field appearance="outline"> </mat-form-field>
<mat-label>Telefon 1</mat-label> </div><div>
<input matInput name="phone1" [(ngModel)]="tenant.phone1"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Telefon 1</mat-label>
<mat-form-field appearance="outline"> <input matInput name="phone1" [(ngModel)]="tenant.phone1"/>
<mat-label>Telefon 2</mat-label> </mat-form-field>
<input matInput name="phone2" [(ngModel)]="tenant.phone2"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>Telefon 2</mat-label>
</div><div> <input matInput name="phone2" [(ngModel)]="tenant.phone2"/>
<mat-form-field appearance="outline"> </mat-form-field>
<mat-label>IBAN</mat-label> </div><div>
<input matInput name="iban" [(ngModel)]="tenant.iban"/> <mat-form-field appearance="outline">
</mat-form-field> <mat-label>IBAN</mat-label>
</div><div> <input matInput name="iban" [(ngModel)]="tenant.iban"/>
<mat-form-field appearance="outline"> </mat-form-field>
<mat-label>Account ID</mat-label> </div>
<input matInput name="account_id" [readonly]="true" [ngModel]="account.id"/> <!--
</mat-form-field> <div>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Account Description</mat-label> <mat-label>Account ID</mat-label>
<input matInput name="account_desc" [readonly]="true" [ngModel]="account.description"/> <input matInput name="account_id" [readonly]="true" [ngModel]="account.id"/>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Account Description</mat-label>
<input matInput name="account_desc" [readonly]="true" [ngModel]="account.description"/>
</mat-form-field>
</div>
-->
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button>
</form>
</div> </div>
<button #submitButton type="submit" mat-raised-button color="primary">Speichern</button> </mat-expansion-panel>
</form> </mat-accordion>
</div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<app-note [selectedTenantId]="tenantId"></app-note>
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
Mietverhältnisse
</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-accordion>
<mat-expansion-panel (opened)="collapseTenancies = true"
(closed)="collapseTenancies = false">
<mat-expansion-panel-header>
<mat-panel-title *ngIf="!collapseTenancies">
Übersicht
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
<div>
<table mat-table [dataSource]="tenancyDataSource" #zftable>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.rawTenancy.description}}</td>
</ng-container>
<ng-container matColumnDef="flat">
<th mat-header-cell *matHeaderCellDef>Wohnung</th>
<td mat-cell *matCellDef="let element">{{element.flat}}</td>
</ng-container>
<ng-container matColumnDef="parking">
<th mat-header-cell *matHeaderCellDef>Garage</th>
<td mat-cell *matCellDef="let element">{{element.parking}}</td>
</ng-container>
<ng-container matColumnDef="commercial_premise">
<th mat-header-cell *matHeaderCellDef>Büro</th>
<td mat-cell *matCellDef="let element">{{element.commercial_premise}}</td>
</ng-container>
<ng-container matColumnDef="startdate">
<th mat-header-cell *matHeaderCellDef>Beginn</th>
<td mat-cell *matCellDef="let element">{{element.rawTenancy.startdate | date}}</td>
</ng-container>
<ng-container matColumnDef="enddate">
<th mat-header-cell *matHeaderCellDef>Ende</th>
<td mat-cell *matCellDef="let element">{{element.rawTenancy.enddate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="tenancyDisplayColumns"></tr>
<tr mat-row *matRowDef="let row; columns: tenancyDisplayColumns;" (click)="setSelectedTenancy(row.rawTenancy)"></tr>
</table>
</div>
</mat-expansion-panel>
</mat-accordion>
</mat-card-content>
</mat-card>
<mat-card class="defaultCard" *ngIf="selectedTenancy">
<mat-card-header>
<mat-card-title>
<span>{{selectedTenancy.description}}</span>
<button mat-icon-button color="accent" aria-label="Schließen" (click)="clearSelectedTenancy()">
<mat-icon>done</mat-icon>
</button>
</mat-card-title>
</mat-card-header>
<mat-card-content>
Mietdetails: {{selectedTenancy.description}}
<div>
<form (ngSubmit)="setTenancyEndData()">
<mat-form-field appearance="outline" id="setenddatefield">
<mat-label>Ende</mat-label>
<input matInput name="enddate" [(ngModel)]="selectedTenancy.enddate" [matDatepicker]="enddatepicker"/>
<mat-datepicker-toggle matSuffix [for]="enddatepicker"></mat-datepicker-toggle>
<mat-datepicker #enddatepicker></mat-datepicker>
</mat-form-field>
<button #setTenancyEndDataButton type="submit" mat-raised-button color="primary">Enddatum setzen</button>
</form>
</div>
<div>
<table mat-table [dataSource]="mappedFeesDataSource" #zftable>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Beschreibung</th>
<td mat-cell *matCellDef="let element">{{element.description}}</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Betrag</th>
<td mat-cell *matCellDef="let element">{{element.amount | number:'1.2-2'}} €</td>
</ng-container>
<ng-container matColumnDef="fee_type">
<th mat-header-cell *matHeaderCellDef>Typ</th>
<td mat-cell *matCellDef="let element">{{element.fee_type}}</td>
</ng-container>
<ng-container matColumnDef="startdate">
<th mat-header-cell *matHeaderCellDef>Beginn</th>
<td mat-cell *matCellDef="let element">{{element.startdate | date}}</td>
</ng-container>
<ng-container matColumnDef="enddate">
<th mat-header-cell *matHeaderCellDef>Ende</th>
<td mat-cell *matCellDef="let element">{{element.enddate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="mappedFeesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: mappedFeesDisplayedColumns;"></tr>
</table>
</div>
<div>
<form (ngSubmit)="addFee()">
<mat-form-field appearance="standard" id="addfeefield">
<mat-label>Mietsatz</mat-label>
<mat-select #mapSelect [(ngModel)]="selectedFee" name="fee">
<mat-option *ngFor="let p of allFees" [value]="p.id">{{p.description}} {{p.amount}} {{p.startdate}}</mat-option>
</mat-select>
</mat-form-field>
<button #mapFeeButton type="submit" mat-raised-button color="primary">Zuordnen</button>
</form>
</div>
</mat-card-content>
</mat-card>
<app-account [selectedAccountId]="tenant.account" [shallBeRentPayment]="true"></app-account>
</section> </section>

View File

@ -1,9 +1,19 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AccountService, TenantService } from '../data-object-service'; import { AccountService, CommercialPremiseService, FeeService, FlatService, ParkingService, PremiseService, TenancyFeeMappingService, TenancyService, TenantService } from '../data-object-service';
import { Account, Tenant } from '../data-objects'; import { Account, CommercialPremise, Fee, Flat, NULL_Account, NULL_CommercialPremise, NULL_Flat, NULL_Parking, NULL_Tenancy, NULL_Tenant, Parking, Premise, Tenancy, TenancyFeeMapping, Tenant } from '../data-objects';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { MatTableDataSource } from '@angular/material/table';
import { ExtApiService } from '../ext-data-object-service';
interface DN_Tenancy {
rawTenancy: Tenancy
flat: string
parking: string
commercialPremise: string
}
@Component({ @Component({
selector: 'app-tenant-details', selector: 'app-tenant-details',
@ -12,32 +22,40 @@ import { MatButton } from '@angular/material/button';
}) })
export class TenantDetailsComponent implements OnInit { export class TenantDetailsComponent implements OnInit {
tenant: Tenant = { tenant: Tenant = NULL_Tenant
id: 0, tenantId: number
salutation: '',
firstname: '', account: Account = NULL_Account
lastname: '',
address1: '', tenancies: DN_Tenancy[]
address2: '', tenancyDataSource: MatTableDataSource<DN_Tenancy>
address3: '', tenancyDisplayColumns: string[] = [ "description", "flat", "parking", "commercial_premise", "startdate", "enddate" ]
zip: '',
city: '', collapseTenantDetails: boolean = false
phone1: '', collapseTenancies: boolean = false
phone2: '',
iban: '', selectedTenancy: Tenancy = undefined
account: 0 mappedFees: Fee[]
} mappedFeesDataSource: MatTableDataSource<Fee>
mappedFeesDisplayedColumns: string[] = [ "description", "amount", "fee_type", "startdate", "enddate" ]
allFees: Fee[]
selectedFee: number
account: Account = {
id: 0,
description: ''
}
@ViewChild('submitButton') submitButton: MatButton @ViewChild('submitButton') submitButton: MatButton
@ViewChild('mapFeeButton') mapFeeButton: MatButton
constructor( constructor(
private tenantService: TenantService, private tenantService: TenantService,
private accountService: AccountService, private accountService: AccountService,
private tenancyService: TenancyService,
private flatService: FlatService,
private parkingService: ParkingService,
private commercialPremiseService: CommercialPremiseService,
private tenancyFeeMappingService: TenancyFeeMappingService,
private feeService: FeeService,
private extApiService: ExtApiService,
private premiseService: PremiseService,
private messageService: MessageService, private messageService: MessageService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router private router: Router
@ -47,36 +65,118 @@ export class TenantDetailsComponent implements OnInit {
try { try {
const id = +this.route.snapshot.paramMap.get('id') const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) { if (id != 0) {
this.tenantId = id
this.tenant = await this.tenantService.getTenant(id) this.tenant = await this.tenantService.getTenant(id)
this.account = await this.accountService.getAccount(this.tenant.account) this.account = await this.accountService.getAccount(this.tenant.account)
this.getTenancies()
} }
} catch (err) { } catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4)) this.messageService.add(JSON.stringify(err, undefined, 4))
} }
} }
async saveTenant() { async getTenancies(): Promise<void> {
this.submitButton.disabled = true try {
this.messageService.add("saveTenant") this.tenancies = []
this.messageService.add(JSON.stringify(this.tenant, undefined, 4)) const premises: Premise[] = await this.premiseService.getPremises()
if (this.tenant.id == 0) { const premisesDict = new Map<number, Premise>()
this.messageService.add("about to insert new tenant") for (let p of premises) {
this.account = { premisesDict.set(p.id, p)
"id": 0,
"description": `account_${this.tenant.firstname}_${this.tenant.lastname}`
} }
this.account = await this.accountService.postAccount(this.account) for (let t of await this.tenancyService.getTenancysByTenant(this.tenant.id)) {
this.tenant.account = this.account.id const flat: Flat = (t.flat) ? await this.flatService.getFlat(t.flat) : NULL_Flat
this.tenant = await this.tenantService.postTenant(this.tenant) const parking: Parking = (t.parking) ? await this.parkingService.getParking(t.parking) : NULL_Parking
this.messageService.add(`Successfully added account with id ${this.account.id} and tenant with id ${this.tenant.id}`) const commercialPremise: CommercialPremise = (t.commercial_premise) ? await this.commercialPremiseService.getCommercialPremise(t.commercial_premise) : NULL_CommercialPremise
} else { this.tenancies.push({
this.messageService.add("about to update existing tenant") rawTenancy: t,
flat: (flat != NULL_Flat) ? `${flat.description} (${premisesDict.get(flat.premise).description})` : '',
parking: (parking != NULL_Parking) ? `${parking.description} (${premisesDict.get(parking.premise).description})` : '',
commercialPremise: (commercialPremise != NULL_CommercialPremise) ? `${commercialPremise.description} (${premisesDict.get(commercialPremise.premise).description})` : ''
})
}
this.tenancyDataSource = new MatTableDataSource<DN_Tenancy>(this.tenancies)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async saveTenant(): Promise<void> {
try {
this.submitButton.disabled = true
this.messageService.add("saveTenant")
this.messageService.add(JSON.stringify(this.tenant, undefined, 4))
if (this.tenant.id == 0) {
this.messageService.add("about to insert new tenant")
this.account = {
"id": 0,
"description": `account_${this.tenant.firstname}_${this.tenant.lastname}`
}
this.account = await this.accountService.postAccount(this.account)
this.tenant.account = this.account.id
this.tenant = await this.tenantService.postTenant(this.tenant)
this.messageService.add(`Successfully added account with id ${this.account.id} and tenant with id ${this.tenant.id}`)
} else {
this.messageService.add("about to update existing tenant")
for (const k in this.tenant) {
this.tenant[k] = (this.tenant[k] == undefined) ? '' : this.tenant[k]
}
this.tenant = await this.tenantService.putTenant(this.tenant)
this.messageService.add(`Successfully changed tenant with id ${this.tenant.id}`)
}
this.router.navigate(['/tenants'])
} finally {
this.submitButton.disabled = false
}
}
async setSelectedTenancy(selectedTenancy: Tenancy): Promise<void> {
this.selectedTenancy = selectedTenancy
this.getMappedFees()
}
async getMappedFees(): Promise<void> {
this.mappedFees = await this.extApiService.getFeeByTenancies(this.selectedTenancy.id)
this.messageService.add(`setSelectedTenancy: mappedFees: ${JSON.stringify(this.mappedFees, undefined, 4)}`)
this.mappedFeesDataSource = new MatTableDataSource<Fee>(this.mappedFees)
this.messageService.add("mappedFeesDataSource set")
}
clearSelectedTenancy(): void {
this.selectedTenancy = undefined
}
async getFees(): Promise<void> {
try {
this.messageService.add("Trying to load fees")
this.allFees = await this.feeService.getFees()
this.messageService.add("fees loaded")
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async addFee() {
try {
this.mapFeeButton.disabled = true
this.messageService.add(`fee: ${ JSON.stringify(this.selectedFee, undefined, 4) }`)
let newMapping: TenancyFeeMapping = {
'tenancy': this.selectedTenancy.id,
'fee': this.selectedFee,
'id': 0
}
newMapping = await this.tenancyFeeMappingService.postTenancyFeeMapping(newMapping)
this.messageService.add(`New fee tenancy mapping created: ${newMapping.id}`)
this.selectedFee = undefined
this.getMappedFees()
} finally {
this.mapFeeButton.disabled = false
} }
this.router.navigate(['/tenants'])
} }
ngOnInit(): void { ngOnInit(): void {
this.getTenant() this.getTenant()
this.getFees()
} }
} }

View File

@ -1,15 +1,28 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MessageService } from './message.service'; import { MessageService } from './message.service';
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { UserCreds } from './userCreds' import { UserCreds } from './userCreds'
import jwt_decode from 'jwt-decode' import jwt_decode from 'jwt-decode'
import { Observable, interval, Subject, Subscription } from 'rxjs'
import { map, takeWhile } from 'rxjs/operators'
import { authserviceBaseUrl, applicationId } from './config'
interface TokenTuple {
authToken: string
refreshToken: string
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class TokenService { export class TokenService {
public static Id_Token_Key : string = "id_token"; public static Id_AuthToken_Key : string = "id_authtoken";
public static Id_RefreshToken_Key : string = "id_refreshtoken";
private _expiryTime = new Subject<number>()
private subscription: Subscription
constructor(private http: HttpClient, private messageService: MessageService) { constructor(private http: HttpClient, private messageService: MessageService) {
} }
@ -17,7 +30,7 @@ export class TokenService {
checkAuthenticated(): boolean { checkAuthenticated(): boolean {
let result: boolean = false let result: boolean = false
const token = localStorage.getItem(TokenService.Id_Token_Key) const token = localStorage.getItem(TokenService.Id_AuthToken_Key)
if (token) { if (token) {
let expiration = jwt_decode(token)["exp"] let expiration = jwt_decode(token)["exp"]
if ((expiration * 1000) > Date.now()) { if ((expiration * 1000) > Date.now()) {
@ -30,25 +43,62 @@ export class TokenService {
return result return result
} }
logout() { logout() : void {
localStorage.removeItem(TokenService.Id_Token_Key) localStorage.removeItem(TokenService.Id_AuthToken_Key)
localStorage.removeItem(TokenService.Id_RefreshToken_Key)
this.messageService.add("Token removed from local storage") this.messageService.add("Token removed from local storage")
} }
async login(login: string, password: string) { get expiryTime(): Observable<number> {
this.messageService.add(`TokenService: trying to login and obtain token`); return this._expiryTime
}
setExpiryTime(token: string) :void {
let exp = jwt_decode(token)["exp"]
let iat = jwt_decode(token)["iat"]
let start = exp - iat
if (this.subscription && !this.subscription.closed) {
this.subscription.unsubscribe()
}
this.subscription = interval(1000).pipe(map(v => start - v)).pipe(takeWhile(v => v != 0)).subscribe(v => this._expiryTime.next(v))
}
async login(login: string, password: string) : Promise<void> {
this.messageService.add(`TokenService: trying to login and obtain token`)
const userCreds : UserCreds = { const userCreds : UserCreds = {
"application": "hv2", "application": applicationId,
"login": login, "login": login,
"password": password "password": password
} }
const token = await this.http.post( const tokenTuple: TokenTuple = await this.http.post<TokenTuple>(
"https://authservice.hottis.de/token", `${authserviceBaseUrl}/refreshable`,
userCreds, userCreds
{responseType:'text'}
).toPromise() ).toPromise()
localStorage.setItem(TokenService.Id_Token_Key, token) localStorage.setItem(TokenService.Id_AuthToken_Key, tokenTuple.authToken)
localStorage.setItem(TokenService.Id_RefreshToken_Key, tokenTuple.refreshToken)
this.messageService.add("Token saved") this.messageService.add("Token saved")
this.setExpiryTime(tokenTuple.authToken)
} }
async refresh() : Promise<void> {
try {
this.messageService.add(`TokenService: trying to refresh tokens`)
const refreshToken = localStorage.getItem(TokenService.Id_RefreshToken_Key)
const tokenTuple: TokenTuple = await this.http.post<TokenTuple>(
`${authserviceBaseUrl}/refresh`,
refreshToken
).toPromise()
localStorage.setItem(TokenService.Id_AuthToken_Key, tokenTuple.authToken)
localStorage.setItem(TokenService.Id_RefreshToken_Key, tokenTuple.refreshToken)
this.messageService.add("Token saved")
this.setExpiryTime(tokenTuple.authToken)
} catch (err) {
this.messageService.add(`error when trying to refresh: ${ JSON.stringify(err, undefined, 4)}`)
localStorage.removeItem(TokenService.Id_AuthToken_Key)
localStorage.removeItem(TokenService.Id_RefreshToken_Key)
this.messageService.add("Token removed from local storage")
}
}
} }

View File

@ -1,3 +1,7 @@
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';
/** /**
* This file includes polyfills needed by Angular and is loaded before the app. * This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file. * You can add your own extra polyfills to this file.

View File

@ -3,7 +3,16 @@
html, body { height: 100%; } html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
table {
width: 75%;
border-spacing: 20px;
}
.spacer {
flex: 1 1 auto;
}
.defaultCard { .defaultCard {
margin: 5px; margin: 5px;
} }