17 Commits
0.0.1 ... 0.0.2

Author SHA1 Message Date
13c9cb4d96 changes for details 2021-09-01 17:58:49 +02:00
5099e4ae63 extend form to handle inserts for tenant 2021-08-31 17:26:12 +02:00
4f8f0fde9d add post call apis to services 2021-08-31 17:25:47 +02:00
6bb1eec181 add post method to api 2021-08-31 17:24:54 +02:00
e238b1fb9f option to insert new tenant 2021-08-31 10:51:08 +02:00
af0e4ffd74 tenant details form 2021-08-30 19:00:27 +02:00
829aefc514 add new pages 2021-08-30 15:55:08 +02:00
083badeacc add CORS 2021-08-30 15:54:41 +02:00
41af716208 docs 2021-08-29 13:39:04 +02:00
f88ec664ea fix path to configuration 2021-08-29 13:17:57 +02:00
8c443b19cd docs 2021-08-29 13:16:50 +02:00
c76b66517b ignore secret configuration 2021-08-29 13:15:53 +02:00
61dc2b28e6 add readme 2021-08-29 12:58:45 +02:00
b65b48a364 generate stuff 2021-08-29 12:50:59 +02:00
2733c5fbac drop generate.sh, generate.py can do it alone using recursive glob 2021-08-29 12:44:38 +02:00
95fbafb217 generated comment 2021-08-28 21:12:08 +02:00
a63076c07c add selector functionality to generate script 2021-08-28 19:00:49 +02:00
57 changed files with 2148 additions and 103 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
__pycache__/
ENV
api/config/dbconfig.ini
api/config/authservice.pub

View File

@ -8,7 +8,7 @@ JWT_PUB_KEY = ""
try:
JWT_PUB_KEY = os.environ["JWT_PUB_KEY"]
except KeyError:
with open('/opt/app/config/authservice.pub', 'r') as f:
with open('./config/authservice.pub', 'r') as f:
JWT_PUB_KEY = f.read()

View File

@ -19,7 +19,7 @@ try:
DB_NAME = os.environ["DB_NAME"]
except KeyError:
config = configparser.ConfigParser()
config.read('/opt/app/config/dbconfig.ini')
config.read('./config/dbconfig.ini')
DB_USER = config["database"]["user"]
DB_PASS = config["database"]["pass"]
DB_HOST = config["database"]["host"]
@ -49,8 +49,9 @@ def execDatabaseOperation(func, params):
sslmode = 'require')
conn.autocommit = False
with conn.cursor(cursor_factory = psycopg2.extras.RealDictCursor) as cur:
return func(cur, params)
with conn:
with conn.cursor(cursor_factory = psycopg2.extras.RealDictCursor) as cur:
return func(cur, params)
except psycopg2.Error as err:
raise Exception("Error when connecting to database: {}".format(err))
finally:
@ -101,3 +102,11 @@ def dbGetOne(user, token_info, params):
except Exception as e:
logger.error(f"Exception: {e}")
raise werkzeug.exceptions.InternalServerError
def dbInsert(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

@ -1,4 +1,12 @@
from db import dbGetMany, dbGetOne
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
from db import dbGetMany, dbGetOne, dbInsert
from loguru import logger
def get_accounts(user, token_info):
return dbGetMany(user, token_info, {
@ -7,11 +15,36 @@ SELECT
id
,description
FROM account_t
ORDER BY
description
""",
"params": ()
}
)
def insert_account(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO account_t
(
description
) VALUES (
%s
)
RETURNING *
""",
"params": [
v_description
]
})
except KeyError as e:
logger.warning("insert_account: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_account(user, token_info, accountId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -42,11 +75,81 @@ SELECT
,iban
,account
FROM tenant_t
ORDER BY
lastname
,firstname
""",
"params": ()
}
)
def insert_tenant(user, token_info, **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"]
v_account = body["account"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO tenant_t
(
salutation
,firstname
,lastname
,address1
,address2
,address3
,zip
,city
,phone1
,phone2
,iban
,account
) VALUES (
%s
,%s
,%s
,%s
,%s
,%s
,%s
,%s
,%s
,%s
,%s
,%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
,v_account
]
})
except KeyError as e:
logger.warning("insert_tenant: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenant(user, token_info, tenantId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -80,11 +183,48 @@ SELECT
,zip
,city
FROM premise_t
ORDER BY
description
""",
"params": ()
}
)
def insert_premise(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_street = body["street"]
v_zip = body["zip"]
v_city = body["city"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO premise_t
(
description
,street
,zip
,city
) VALUES (
%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_street
,v_zip
,v_city
]
})
except KeyError as e:
logger.warning("insert_premise: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_premise(user, token_info, premiseId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -110,11 +250,49 @@ SELECT
,area
,flat_no
FROM flat_t
ORDER BY
premise
,description
""",
"params": ()
}
)
def insert_flat(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
v_area = body["area"]
v_flat_no = body["flat_no"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO flat_t
(
description
,premise
,area
,flat_no
) VALUES (
%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_premise
,v_area
,v_flat_no
]
})
except KeyError as e:
logger.warning("insert_flat: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_flat(user, token_info, flatId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -140,11 +318,49 @@ SELECT
,startdate
,enddate
FROM overhead_advance_t
ORDER BY
description
,startdate
""",
"params": ()
}
)
def insert_overhead_advance(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_amount = body["amount"]
v_startdate = body["startdate"]
v_enddate = body["enddate"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO overhead_advance_t
(
description
,amount
,startdate
,enddate
) VALUES (
%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_amount
,v_startdate
,v_enddate
]
})
except KeyError as e:
logger.warning("insert_overhead_advance: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_overhead_advance(user, token_info, overhead_advanceId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -168,11 +384,41 @@ SELECT
,overhead_advance
,flat
FROM overhead_advance_flat_mapping_t
ORDER BY
overhead_advance
,flat
""",
"params": ()
}
)
def insert_overhead_advance_flat_mapping(user, token_info, **args):
try:
body = args["body"]
v_overhead_advance = body["overhead_advance"]
v_flat = body["flat"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO overhead_advance_flat_mapping_t
(
overhead_advance
,flat
) VALUES (
%s
,%s
)
RETURNING *
""",
"params": [
v_overhead_advance
,v_flat
]
})
except KeyError as e:
logger.warning("insert_overhead_advance_flat_mapping: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_overhead_advance_flat_mapping(user, token_info, overhead_advance_flat_mappingId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -194,11 +440,41 @@ SELECT
,description
,premise
FROM parking_t
ORDER BY
premise
,description
""",
"params": ()
}
)
def insert_parking(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO parking_t
(
description
,premise
) VALUES (
%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_premise
]
})
except KeyError as e:
logger.warning("insert_parking: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_parking(user, token_info, parkingId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -220,11 +496,41 @@ SELECT
,description
,premise
FROM commercial_premise_t
ORDER BY
premise
,description
""",
"params": ()
}
)
def insert_commercial_premise(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_premise = body["premise"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO commercial_premise_t
(
description
,premise
) VALUES (
%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_premise
]
})
except KeyError as e:
logger.warning("insert_commercial_premise: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_commercial_premise(user, token_info, commercial_premiseId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -251,11 +557,61 @@ SELECT
,startdate
,enddate
FROM tenancy_t
ORDER BY
description
,startdate
""",
"params": ()
}
)
def insert_tenancy(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_tenant = body["tenant"]
v_flat = body["flat"]
v_parking = body["parking"]
v_commercial_premise = body["commercial_premise"]
v_startdate = body["startdate"]
v_enddate = body["enddate"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO tenancy_t
(
description
,tenant
,flat
,parking
,commercial_premise
,startdate
,enddate
) VALUES (
%s
,%s
,%s
,%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_tenant
,v_flat
,v_parking
,v_commercial_premise
,v_startdate
,v_enddate
]
})
except KeyError as e:
logger.warning("insert_tenancy: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenancy(user, token_info, tenancyId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -285,11 +641,53 @@ SELECT
,startdate
,enddate
FROM fee_t
ORDER BY
description
,startdate
""",
"params": ()
}
)
def insert_fee(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_amount = body["amount"]
v_fee_type = body["fee_type"]
v_startdate = body["startdate"]
v_enddate = body["enddate"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO fee_t
(
description
,amount
,fee_type
,startdate
,enddate
) VALUES (
%s
,%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_amount
,v_fee_type
,v_startdate
,v_enddate
]
})
except KeyError as e:
logger.warning("insert_fee: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_fee(user, token_info, feeId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -319,6 +717,33 @@ SELECT
}
)
def insert_tenancy_fee_mapping(user, token_info, **args):
try:
body = args["body"]
v_tenancy = body["tenancy"]
v_fee = body["fee"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO tenancy_fee_mapping_t
(
tenancy
,fee
) VALUES (
%s
,%s
)
RETURNING *
""",
"params": [
v_tenancy
,v_fee
]
})
except KeyError as e:
logger.warning("insert_tenancy_fee_mapping: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_tenancy_fee_mapping(user, token_info, tenancy_fee_mappingId=None):
return dbGetOne(user, token_info, {
"statement": """
@ -347,6 +772,41 @@ SELECT
}
)
def insert_account_entry(user, token_info, **args):
try:
body = args["body"]
v_description = body["description"]
v_account = body["account"]
v_created_at = body["created_at"]
v_amount = body["amount"]
return dbInsert(user, token_info, {
"statement": """
INSERT INTO account_entry_t
(
description
,account
,created_at
,amount
) VALUES (
%s
,%s
,%s
,%s
)
RETURNING *
""",
"params": [
v_description
,v_account
,v_created_at
,v_amount
]
})
except KeyError as e:
logger.warning("insert_account_entry: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_account_entry(user, token_info, account_entryId=None):
return dbGetOne(user, token_info, {
"statement": """

View File

@ -1,4 +1,7 @@
from db import dbGetMany, dbGetOne
$GENERATED_PYTHON_COMMENT
from db import dbGetMany, dbGetOne, dbInsert
from loguru import logger
#for $table in $tables
def get_${table.name}s(user, token_info):
@ -10,11 +13,56 @@ SELECT
,$column.name
#end for
FROM ${table.name}_t
#if $table.selectors
ORDER BY
#set $sep = ""
#for $selector in $table.selectors
$sep$selector
#set $sep = ","
#end for
#end if
""",
"params": ()
}
)
def insert_${table.name}(user, token_info, **args):
try:
body = args["body"]
#for $column in $table.columns
v_$column.name = body["$column.name"]
#end for
return dbInsert(user, token_info, {
"statement": """
INSERT INTO ${table.name}_t
(
#set $sep=""
#for $column in $table.columns
$sep$column.name
#set $sep=","
#end for
) VALUES (
#set $sep=""
#for $column in $table.columns
$sep%s
#set $sep=","
#end for
)
RETURNING *
""",
"params": [
#set $sep=""
#for $column in $table.columns
${sep}v_$column.name
#set $sep=","
#end for
]
})
except KeyError as e:
logger.warning("insert_${table.name}: parameter missing: {}".format(e))
raise werkzeug.exceptions.UnprocessableEntity("parameter missing: {}".format(e))
def get_${table.name}(user, token_info, ${table.name}Id=None):
return dbGetOne(user, token_info, {
"statement": """

View File

@ -1,3 +1,10 @@
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
openapi: 3.0.0
info:
title: hv2-api
@ -28,6 +35,27 @@ paths:
$ref: '#/components/schemas/account'
security:
- jwt: ['secret']
post:
tags: [ "account" ]
summary: Insert a account
operationId: methods.insert_account
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/accounts/{accountId}:
get:
tags: [ "account" ]
@ -66,6 +94,27 @@ paths:
$ref: '#/components/schemas/tenant'
security:
- jwt: ['secret']
post:
tags: [ "tenant" ]
summary: Insert a tenant
operationId: methods.insert_tenant
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/{tenantId}:
get:
tags: [ "tenant" ]
@ -104,6 +153,27 @@ paths:
$ref: '#/components/schemas/premise'
security:
- jwt: ['secret']
post:
tags: [ "premise" ]
summary: Insert a premise
operationId: methods.insert_premise
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/premises/{premiseId}:
get:
tags: [ "premise" ]
@ -142,6 +212,27 @@ paths:
$ref: '#/components/schemas/flat'
security:
- jwt: ['secret']
post:
tags: [ "flat" ]
summary: Insert a flat
operationId: methods.insert_flat
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/{flatId}:
get:
tags: [ "flat" ]
@ -180,6 +271,27 @@ paths:
$ref: '#/components/schemas/overhead_advance'
security:
- jwt: ['secret']
post:
tags: [ "overhead_advance" ]
summary: Insert a overhead_advance
operationId: methods.insert_overhead_advance
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_advances/{overhead_advanceId}:
get:
tags: [ "overhead_advance" ]
@ -218,6 +330,27 @@ paths:
$ref: '#/components/schemas/overhead_advance_flat_mapping'
security:
- jwt: ['secret']
post:
tags: [ "overhead_advance_flat_mapping" ]
summary: Insert a overhead_advance_flat_mapping
operationId: methods.insert_overhead_advance_flat_mapping
requestBody:
description: overhead_advance_flat_mapping
content:
application/json:
schema:
$ref: '#/components/schemas/overhead_advance_flat_mapping'
responses:
'200':
description: overhead_advance_flat_mapping successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/overhead_advance_flat_mapping'
security:
- jwt: ['secret']
/v1/overhead_advance_flat_mappings/{overhead_advance_flat_mappingId}:
get:
tags: [ "overhead_advance_flat_mapping" ]
@ -256,6 +389,27 @@ paths:
$ref: '#/components/schemas/parking'
security:
- jwt: ['secret']
post:
tags: [ "parking" ]
summary: Insert a parking
operationId: methods.insert_parking
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/{parkingId}:
get:
tags: [ "parking" ]
@ -294,6 +448,27 @@ paths:
$ref: '#/components/schemas/commercial_premise'
security:
- jwt: ['secret']
post:
tags: [ "commercial_premise" ]
summary: Insert a commercial_premise
operationId: methods.insert_commercial_premise
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/{commercial_premiseId}:
get:
tags: [ "commercial_premise" ]
@ -332,6 +507,27 @@ paths:
$ref: '#/components/schemas/tenancy'
security:
- jwt: ['secret']
post:
tags: [ "tenancy" ]
summary: Insert a tenancy
operationId: methods.insert_tenancy
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/{tenancyId}:
get:
tags: [ "tenancy" ]
@ -370,6 +566,27 @@ paths:
$ref: '#/components/schemas/fee'
security:
- jwt: ['secret']
post:
tags: [ "fee" ]
summary: Insert a fee
operationId: methods.insert_fee
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/fees/{feeId}:
get:
tags: [ "fee" ]
@ -408,6 +625,27 @@ paths:
$ref: '#/components/schemas/tenancy_fee_mapping'
security:
- jwt: ['secret']
post:
tags: [ "tenancy_fee_mapping" ]
summary: Insert a tenancy_fee_mapping
operationId: methods.insert_tenancy_fee_mapping
requestBody:
description: tenancy_fee_mapping
content:
application/json:
schema:
$ref: '#/components/schemas/tenancy_fee_mapping'
responses:
'200':
description: tenancy_fee_mapping successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/tenancy_fee_mapping'
security:
- jwt: ['secret']
/v1/tenancy_fee_mappings/{tenancy_fee_mappingId}:
get:
tags: [ "tenancy_fee_mapping" ]
@ -446,6 +684,27 @@ paths:
$ref: '#/components/schemas/account_entry'
security:
- jwt: ['secret']
post:
tags: [ "account_entry" ]
summary: Insert a account_entry
operationId: methods.insert_account_entry
requestBody:
description: account_entry
content:
application/json:
schema:
$ref: '#/components/schemas/account_entry'
responses:
'200':
description: account_entry successfully inserted
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/account_entry'
security:
- jwt: ['secret']
/v1/account_entrys/{account_entryId}:
get:
tags: [ "account_entry" ]

View File

@ -1,3 +1,5 @@
$GENERATED_YAML_COMMENT
openapi: 3.0.0
info:
title: hv2-api
@ -29,6 +31,27 @@ paths:
\$ref: '#/components/schemas/$table.name'
security:
- jwt: ['secret']
post:
tags: [ "$table.name" ]
summary: Insert a ${table.name}
operationId: methods.insert_${table.name}
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']
/v1/${table.name}s/{${table.name}Id}:
get:
tags: [ "$table.name" ]

View File

@ -1,14 +0,0 @@
#!/bin/bash
. ENV
IMAGE_NAME="registry.hottis.de/hv2/hv2-api"
VERSION=0.0.x
docker run \
-d \
--rm \
--name "hv2-api" \
-p 5000:5000
${IMAGE_NAME}:${VERSION}

View File

@ -1,8 +1,10 @@
import connexion
import logging
from flask_cors import CORS
logging.basicConfig(level=logging.DEBUG)
app = connexion.App('hv2-api')
app.add_api('./openapi.yaml')
CORS(app.app)
app.run(port=8080)

View File

@ -10,9 +10,9 @@ parser.add_argument('--schema', '-s',
default='./schema.json')
parser.add_argument('--template', '-t',
help="""Template file, templates files must be named as the final output file
with an additional .tmpl extension. Default: all template files in the current folder.""",
with an additional .tmpl extension. Default: all template files recursively from the current folder.""",
required=False,
default="*.tmpl")
default="**/*.tmpl")
args = parser.parse_args()
@ -20,7 +20,8 @@ with open(args.schema) as schemaFile:
schema = json.load(schemaFile)
for table in schema["tables"]:
for column in table["columns"]:
columns = table["columns"]
for column in columns:
if column["sqltype"] == 'serial':
column["apitype"] = 'integer'
column["jstype"] = 'number'
@ -39,8 +40,34 @@ for table in schema["tables"]:
elif column["sqltype"].startswith('numeric'):
column["apitype"] = 'number'
column["jstype"] = 'number'
table["selectors"] = [ y["name"] for y in sorted([ x for x in columns if "selector" in x ], key=lambda x: x["selector"])]
for f in glob.glob(args.template):
schema["GENERATED_SQL_COMMENT"] = """
-- ----------------------------------------
-- THIS FILE HAS BEEN GENERATED
-- DO NOT EDIT MANUALLY
-- ----------------------------------------
"""
schema["GENERATED_PYTHON_COMMENT"] = """
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
"""
schema["GENERATED_YAML_COMMENT"] = """
# -----------------------------------------
# THIS FILE HAS BEEN GENERATED
# DO NOT EDIT MANUALLY
# -----------------------------------------
"""
schema["GENERATED_TS_COMMENT"] = """
// -----------------------------------------
// THIS FILE HAS BEEN GENERATED
// DO NOT EDIT MANUALLY
// -----------------------------------------
"""
for f in glob.glob(args.template, recursive=True):
print(f"process {f}")
tmpl = Template(file=f, searchList=[schema])
with open(f[:-5], 'w') as outFile:

View File

@ -1,5 +0,0 @@
#!/bin/bash
find . -name \*.tmpl -exec python generate.py --schema schema.json --template {} \;

16
readme.md Normal file
View File

@ -0,0 +1,16 @@
# Developing in HV2 umbrella project
## Tools and Templates
* all tools provide should work on Linux and on Windows, shell scripts are avoided
* templates files must be generated at development time, generated files must be put into the repository
* use ``generate.py`` in the umbrella project's root without any arguments to generate all template files in all subdirectories using the ``schema.json`` from the umbrella project's root
## Subprojects
### API
* to start the API set the required env variables (see ``ENV.tmp``) or the configuration files in the subdirectory ``./config`` and run ``test.py`` from the API subproject
* make sure these configuration files are not added to the repository, they contain secrets
* in the development environment the API will answer at ``http://localhost:8080``, the Swagger-UI is available at ``http://localhost:8080/ui``

View File

@ -3,15 +3,15 @@
{
"name": "account",
"columns": [
{ "name": "description", "sqltype": "varchar(128)", "notnull": true }
{ "name": "description", "sqltype": "varchar(128)", "notnull": true, "selector": 0 }
]
},
{
"name": "tenant",
"columns": [
{ "name": "salutation", "sqltype": "varchar(128)" },
{ "name": "firstname", "sqltype": "varchar(128)" },
{ "name": "lastname", "sqltype": "varchar(128)" },
{ "name": "firstname", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "lastname", "sqltype": "varchar(128)", "selector": 0 },
{ "name": "address1", "sqltype": "varchar(128)" },
{ "name": "address2", "sqltype": "varchar(128)" },
{ "name": "address3", "sqltype": "varchar(128)" },
@ -26,7 +26,7 @@
{
"name": "premise",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 },
{ "name": "street", "sqltype": "varchar(128)", "notnull": true },
{ "name": "zip", "sqltype": "varchar(10)", "notnull": true },
{ "name": "city", "sqltype": "varchar(128)", "notnull": true }
@ -35,8 +35,8 @@
{
"name": "flat",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "premise", "sqltype": "integer", "foreignkey": true },
{ "name": "description", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 },
{ "name": "area", "sqltype": "numeric(10,2)", "notnull": true },
{ "name": "flat_no", "sqltype": "integer" }
]
@ -44,42 +44,42 @@
{
"name": "overhead_advance",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 },
{ "name": "amount", "sqltype": "numeric(10,4)", "notnull": true },
{ "name": "startdate", "sqltype": "date" },
{ "name": "startdate", "sqltype": "date", "selector": 1 },
{ "name": "enddate", "sqltype": "date" }
]
},
{
"name": "overhead_advance_flat_mapping",
"columns": [
{ "name": "overhead_advance", "sqltype": "integer", "notnull": true, "foreignkey": true },
{ "name": "flat", "sqltype": "integer", "notnull": true, "foreignkey": true }
{ "name": "overhead_advance", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 0 },
{ "name": "flat", "sqltype": "integer", "notnull": true, "foreignkey": true, "selector": 1 }
]
},
{
"name": "parking",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "premise", "sqltype": "integer", "foreignkey": true }
{ "name": "description", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 }
]
},
{
"name": "commercial_premise",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "premise", "sqltype": "integer", "foreignkey": true }
{ "name": "description", "sqltype": "varchar(128)", "selector": 1 },
{ "name": "premise", "sqltype": "integer", "foreignkey": true, "selector": 0 }
]
},
{
"name": "tenancy",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 },
{ "name": "tenant", "sqltype": "integer", "notnull": true, "foreignkey": true },
{ "name": "flat", "sqltype": "integer", "notnull": false, "foreignkey": true },
{ "name": "parking", "sqltype": "integer", "notnull": false, "foreignkey": true },
{ "name": "commercial_premise", "sqltype": "integer", "notnull": false, "foreignkey": true },
{ "name": "startdate", "sqltype": "date", "notnull": true },
{ "name": "startdate", "sqltype": "date", "notnull": true, "selector": 1 },
{ "name": "enddate", "sqltype": "date", "notnull": false }
],
"tableConstraints": [
@ -89,10 +89,10 @@
{
"name": "fee",
"columns": [
{ "name": "description", "sqltype": "varchar(128)" },
{ "name": "description", "sqltype": "varchar(128)", "selector": 0 },
{ "name": "amount", "sqltype": "numeric(10,2)", "notnull": true },
{ "name": "fee_type", "sqltype": "varchar(10)", "notnull": true },
{ "name": "startdate", "sqltype": "date" },
{ "name": "startdate", "sqltype": "date", "selector": 1 },
{ "name": "enddate", "sqltype": "date" }
],
"tableConstraints": [

View File

@ -1,3 +1,10 @@
-- ----------------------------------------
-- THIS FILE HAS BEEN GENERATED
-- DO NOT EDIT MANUALLY
-- ----------------------------------------
CREATE TABLE account_t (
id serial not null primary key
,description varchar(128) not null

View File

@ -1,3 +1,5 @@
$GENERATED_SQL_COMMENT
#for $table in $tables
CREATE TABLE ${table.name}_t (
id serial not null primary key

View File

@ -9,7 +9,6 @@
"lint": "ng lint",
"e2e": "ng e2e",
"generate": "python ../helpers/hv2-api/generate.py -s ../helpers/hv2-api/schema.json -t ./src/app/*.tmpl"
},
"private": true,
"dependencies": {

View File

@ -3,12 +3,28 @@ import { RouterModule, Routes } from '@angular/router';
import { AuthGuardService } from './auth-guard.service';
import { LoginComponent } from './login/login.component';
import { LogoutComponent } from './logout/logout.component';
import { TestOutputComponent } from './test-output/test-output.component';
import { MyTenantsComponent } from './my-tenants/my-tenants.component';
import { MyPremisesComponent } from './my-premises/my-premises.component';
import { MyFlatsComponent } from './my-flats/my-flats.component';
import { MyParkingsComponent } from './my-parkings/my-parkings.component';
import { MyCommercialUnitsComponent } from './my-commercial-units/my-commercial-units.component';
import { TenantDetailsComponent } from './tenant-details/tenant-details.component';
import { PremiseDetailsComponent } from './premise-details/premise-details.component';
import { FlatDetailsComponent } from './flat-details/flat-details.component';
const routes: Routes = [
{ path: 'test', component: TestOutputComponent, canActivate: [ AuthGuardService ] },
{ path: 'tenants', component: MyTenantsComponent, canActivate: [ AuthGuardService ] },
{ path: 'premises', component: MyPremisesComponent, canActivate: [ AuthGuardService ] },
{ path: 'flats', component: MyFlatsComponent, canActivate: [ AuthGuardService ] },
{ path: 'parkings', component: MyParkingsComponent, canActivate: [ AuthGuardService ] },
{ path: 'commercialunits', component: MyCommercialUnitsComponent, canActivate: [ AuthGuardService ] },
{ path: 'tenant/:id', component: TenantDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'tenant', component: TenantDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'premise/:id', component: PremiseDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'premise', component: PremiseDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'flat/:id', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'flat', component: FlatDetailsComponent, canActivate: [ AuthGuardService ] },
{ path: 'logout', component: LogoutComponent },
{ path: 'login', component: LoginComponent }
]

View File

@ -21,8 +21,17 @@ import { LogoutComponent } from './logout/logout.component';
import { LoginComponent } from './login/login.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field'
import { MyTenantsComponent } from './my-tenants/my-tenants.component';
import { MatTableModule } from '@angular/material/table';
import { MyPremisesComponent } from './my-premises/my-premises.component';
import { MyFlatsComponent } from './my-flats/my-flats.component';
import { MyParkingsComponent } from './my-parkings/my-parkings.component';
import { MyCommercialUnitsComponent } from './my-commercial-units/my-commercial-units.component';
import { TenantDetailsComponent } from './tenant-details/tenant-details.component';
import { PremiseDetailsComponent } from './premise-details/premise-details.component';
import { FlatDetailsComponent } from './flat-details/flat-details.component';
import { MatSelectModule } from '@angular/material/select'
@NgModule({
declarations: [
AppComponent,
@ -30,7 +39,15 @@ import { MatInputModule } from '@angular/material/input';
MessagesComponent,
TestOutputComponent,
LogoutComponent,
LoginComponent
LoginComponent,
MyTenantsComponent,
MyPremisesComponent,
MyFlatsComponent,
MyParkingsComponent,
MyCommercialUnitsComponent,
TenantDetailsComponent,
PremiseDetailsComponent,
FlatDetailsComponent
],
imports: [
BrowserModule,
@ -46,7 +63,10 @@ import { MatInputModule } from '@angular/material/input';
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
MatInputModule
MatTableModule,
MatInputModule,
MatFormFieldModule,
MatSelectModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerInterceptor, multi: true },

View File

@ -8,6 +8,8 @@ import {
import { Observable } from 'rxjs';
import { MessageService } from './message.service';
import { TokenService } from './token.service';
import { serviceBaseUrl } from './config';
@Injectable()
export class AuthHandlerInterceptor implements HttpInterceptor {
@ -16,7 +18,7 @@ export class AuthHandlerInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const token = localStorage.getItem(TokenService.Id_Token_Key)
if (request.url.includes('api.hv.nober.de') && token) {
if (request.url.includes(serviceBaseUrl) && token) {
const clone = request.clone({
setHeaders: { Authorization: `Bearer ${token}`}
})

View File

@ -1,2 +1,4 @@
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://localhost:8080";

View File

@ -1,3 +1,11 @@
// -----------------------------------------
// THIS FILE HAS BEEN GENERATED
// DO NOT EDIT MANUALLY
// -----------------------------------------
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@ -20,121 +28,274 @@ import { TenancyFeeMapping } from './data-objects';
import { AccountEntry } from './data-objects';
@Injectable({
providedIn: 'root'
})
@Injectable({ providedIn: 'root' })
export class AccountService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getAccount(): Promise<Account> {
this.messageService.add(`AccountService: fetch data`);
return this.http.get<Account>(`${serviceBaseUrl}/v1/account`).toPromise()
async getAccounts(): Promise<Account[]> {
this.messageService.add(`AccountService: get data`);
return this.http.get<Account[]>(`${serviceBaseUrl}/v1/accounts`).toPromise()
}
async getAccount(id: number): Promise<Account> {
this.messageService.add(`AccountService: get data for ${id}`);
return this.http.get<Account>(`${serviceBaseUrl}/v1/accounts/${id}`).toPromise()
}
async postAccount(item: Account): Promise<Account> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`AccountService: post data for ${itemStr}`);
return this.http.post<Account>(`${serviceBaseUrl}/v1/accounts`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class TenantService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getTenant(): Promise<Tenant> {
this.messageService.add(`TenantService: fetch data`);
return this.http.get<Tenant>(`${serviceBaseUrl}/v1/tenant`).toPromise()
async getTenants(): Promise<Tenant[]> {
this.messageService.add(`TenantService: get data`);
return this.http.get<Tenant[]>(`${serviceBaseUrl}/v1/tenants`).toPromise()
}
async getTenant(id: number): Promise<Tenant> {
this.messageService.add(`TenantService: get data for ${id}`);
return this.http.get<Tenant>(`${serviceBaseUrl}/v1/tenants/${id}`).toPromise()
}
async postTenant(item: Tenant): Promise<Tenant> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`TenantService: post data for ${itemStr}`);
return this.http.post<Tenant>(`${serviceBaseUrl}/v1/tenants`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class PremiseService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getPremise(): Promise<Premise> {
this.messageService.add(`PremiseService: fetch data`);
return this.http.get<Premise>(`${serviceBaseUrl}/v1/premise`).toPromise()
async getPremises(): Promise<Premise[]> {
this.messageService.add(`PremiseService: get data`);
return this.http.get<Premise[]>(`${serviceBaseUrl}/v1/premises`).toPromise()
}
async getPremise(id: number): Promise<Premise> {
this.messageService.add(`PremiseService: get data for ${id}`);
return this.http.get<Premise>(`${serviceBaseUrl}/v1/premises/${id}`).toPromise()
}
async postPremise(item: Premise): Promise<Premise> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`PremiseService: post data for ${itemStr}`);
return this.http.post<Premise>(`${serviceBaseUrl}/v1/premises`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class FlatService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getFlat(): Promise<Flat> {
this.messageService.add(`FlatService: fetch data`);
return this.http.get<Flat>(`${serviceBaseUrl}/v1/flat`).toPromise()
async getFlats(): Promise<Flat[]> {
this.messageService.add(`FlatService: get data`);
return this.http.get<Flat[]>(`${serviceBaseUrl}/v1/flats`).toPromise()
}
async getFlat(id: number): Promise<Flat> {
this.messageService.add(`FlatService: get data for ${id}`);
return this.http.get<Flat>(`${serviceBaseUrl}/v1/flats/${id}`).toPromise()
}
async postFlat(item: Flat): Promise<Flat> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`FlatService: post data for ${itemStr}`);
return this.http.post<Flat>(`${serviceBaseUrl}/v1/flats`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class OverheadAdvanceService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getOverheadAdvance(): Promise<OverheadAdvance> {
this.messageService.add(`OverheadAdvanceService: fetch data`);
return this.http.get<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advance`).toPromise()
async getOverheadAdvances(): Promise<OverheadAdvance[]> {
this.messageService.add(`OverheadAdvanceService: get data`);
return this.http.get<OverheadAdvance[]>(`${serviceBaseUrl}/v1/overhead_advances`).toPromise()
}
async getOverheadAdvance(id: number): Promise<OverheadAdvance> {
this.messageService.add(`OverheadAdvanceService: get data for ${id}`);
return this.http.get<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advances/${id}`).toPromise()
}
async postOverheadAdvance(item: OverheadAdvance): Promise<OverheadAdvance> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`OverheadAdvanceService: post data for ${itemStr}`);
return this.http.post<OverheadAdvance>(`${serviceBaseUrl}/v1/overhead_advances`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class OverheadAdvanceFlatMappingService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getOverheadAdvanceFlatMapping(): Promise<OverheadAdvanceFlatMapping> {
this.messageService.add(`OverheadAdvanceFlatMappingService: fetch data`);
return this.http.get<OverheadAdvanceFlatMapping>(`${serviceBaseUrl}/v1/overhead_advance_flat_mapping`).toPromise()
async getOverheadAdvanceFlatMappings(): Promise<OverheadAdvanceFlatMapping[]> {
this.messageService.add(`OverheadAdvanceFlatMappingService: get data`);
return this.http.get<OverheadAdvanceFlatMapping[]>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings`).toPromise()
}
async getOverheadAdvanceFlatMapping(id: number): Promise<OverheadAdvanceFlatMapping> {
this.messageService.add(`OverheadAdvanceFlatMappingService: get data for ${id}`);
return this.http.get<OverheadAdvanceFlatMapping>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings/${id}`).toPromise()
}
async postOverheadAdvanceFlatMapping(item: OverheadAdvanceFlatMapping): Promise<OverheadAdvanceFlatMapping> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`OverheadAdvanceFlatMappingService: post data for ${itemStr}`);
return this.http.post<OverheadAdvanceFlatMapping>(`${serviceBaseUrl}/v1/overhead_advance_flat_mappings`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class ParkingService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getParking(): Promise<Parking> {
this.messageService.add(`ParkingService: fetch data`);
return this.http.get<Parking>(`${serviceBaseUrl}/v1/parking`).toPromise()
async getParkings(): Promise<Parking[]> {
this.messageService.add(`ParkingService: get data`);
return this.http.get<Parking[]>(`${serviceBaseUrl}/v1/parkings`).toPromise()
}
async getParking(id: number): Promise<Parking> {
this.messageService.add(`ParkingService: get data for ${id}`);
return this.http.get<Parking>(`${serviceBaseUrl}/v1/parkings/${id}`).toPromise()
}
async postParking(item: Parking): Promise<Parking> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`ParkingService: post data for ${itemStr}`);
return this.http.post<Parking>(`${serviceBaseUrl}/v1/parkings`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class CommercialPremiseService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getCommercialPremise(): Promise<CommercialPremise> {
this.messageService.add(`CommercialPremiseService: fetch data`);
return this.http.get<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premise`).toPromise()
async getCommercialPremises(): Promise<CommercialPremise[]> {
this.messageService.add(`CommercialPremiseService: get data`);
return this.http.get<CommercialPremise[]>(`${serviceBaseUrl}/v1/commercial_premises`).toPromise()
}
async getCommercialPremise(id: number): Promise<CommercialPremise> {
this.messageService.add(`CommercialPremiseService: get data for ${id}`);
return this.http.get<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premises/${id}`).toPromise()
}
async postCommercialPremise(item: CommercialPremise): Promise<CommercialPremise> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`CommercialPremiseService: post data for ${itemStr}`);
return this.http.post<CommercialPremise>(`${serviceBaseUrl}/v1/commercial_premises`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class TenancyService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getTenancy(): Promise<Tenancy> {
this.messageService.add(`TenancyService: fetch data`);
return this.http.get<Tenancy>(`${serviceBaseUrl}/v1/tenancy`).toPromise()
async getTenancys(): Promise<Tenancy[]> {
this.messageService.add(`TenancyService: get data`);
return this.http.get<Tenancy[]>(`${serviceBaseUrl}/v1/tenancys`).toPromise()
}
async getTenancy(id: number): Promise<Tenancy> {
this.messageService.add(`TenancyService: get data for ${id}`);
return this.http.get<Tenancy>(`${serviceBaseUrl}/v1/tenancys/${id}`).toPromise()
}
async postTenancy(item: Tenancy): Promise<Tenancy> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`TenancyService: post data for ${itemStr}`);
return this.http.post<Tenancy>(`${serviceBaseUrl}/v1/tenancys`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class FeeService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getFee(): Promise<Fee> {
this.messageService.add(`FeeService: fetch data`);
return this.http.get<Fee>(`${serviceBaseUrl}/v1/fee`).toPromise()
async getFees(): Promise<Fee[]> {
this.messageService.add(`FeeService: get data`);
return this.http.get<Fee[]>(`${serviceBaseUrl}/v1/fees`).toPromise()
}
async getFee(id: number): Promise<Fee> {
this.messageService.add(`FeeService: get data for ${id}`);
return this.http.get<Fee>(`${serviceBaseUrl}/v1/fees/${id}`).toPromise()
}
async postFee(item: Fee): Promise<Fee> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`FeeService: post data for ${itemStr}`);
return this.http.post<Fee>(`${serviceBaseUrl}/v1/fees`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class TenancyFeeMappingService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getTenancyFeeMapping(): Promise<TenancyFeeMapping> {
this.messageService.add(`TenancyFeeMappingService: fetch data`);
return this.http.get<TenancyFeeMapping>(`${serviceBaseUrl}/v1/tenancy_fee_mapping`).toPromise()
async getTenancyFeeMappings(): Promise<TenancyFeeMapping[]> {
this.messageService.add(`TenancyFeeMappingService: get data`);
return this.http.get<TenancyFeeMapping[]>(`${serviceBaseUrl}/v1/tenancy_fee_mappings`).toPromise()
}
async getTenancyFeeMapping(id: number): Promise<TenancyFeeMapping> {
this.messageService.add(`TenancyFeeMappingService: get data for ${id}`);
return this.http.get<TenancyFeeMapping>(`${serviceBaseUrl}/v1/tenancy_fee_mappings/${id}`).toPromise()
}
async postTenancyFeeMapping(item: TenancyFeeMapping): Promise<TenancyFeeMapping> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`TenancyFeeMappingService: post data for ${itemStr}`);
return this.http.post<TenancyFeeMapping>(`${serviceBaseUrl}/v1/tenancy_fee_mappings`, item).toPromise()
}
}
@Injectable({ providedIn: 'root' })
export class AccountEntryService {
constructor(private messageService: MessageService, private http: HttpClient) { }
async getAccountEntry(): Promise<AccountEntry> {
this.messageService.add(`AccountEntryService: fetch data`);
return this.http.get<AccountEntry>(`${serviceBaseUrl}/v1/account_entry`).toPromise()
async getAccountEntrys(): Promise<AccountEntry[]> {
this.messageService.add(`AccountEntryService: get data`);
return this.http.get<AccountEntry[]>(`${serviceBaseUrl}/v1/account_entrys`).toPromise()
}
async getAccountEntry(id: number): Promise<AccountEntry> {
this.messageService.add(`AccountEntryService: get data for ${id}`);
return this.http.get<AccountEntry>(`${serviceBaseUrl}/v1/account_entrys/${id}`).toPromise()
}
async postAccountEntry(item: AccountEntry): Promise<AccountEntry> {
let itemStr: string = JSON.stringify(item, undefined, 4)
this.messageService.add(`AccountEntryService: post data for ${itemStr}`);
return this.http.post<AccountEntry>(`${serviceBaseUrl}/v1/account_entrys`, item).toPromise()
}
}

View File

@ -1,3 +1,6 @@
$GENERATED_TS_COMMENT
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@ -11,9 +14,6 @@ import { ${JsNameConverter($table.name)} } from './data-objects';
#end for
@Injectable({
providedIn: 'root'
})
@ -22,13 +22,26 @@ import { ${JsNameConverter($table.name)} } from './data-objects';
#from generateHelper import JsNameConverter
#for $table in $tables
@Injectable({ providedIn: 'root' })
export class ${JsNameConverter($table.name)}Service {
constructor(private messageService: MessageService, private http: HttpClient) { }
async get${JsNameConverter($table.name)}(): Promise<${JsNameConverter($table.name)}> {
this.messageService.add(`${JsNameConverter($table.name)}Service: fetch data`);
return this.http.get<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}`).toPromise()
async get${JsNameConverter($table.name)}s(): Promise<${JsNameConverter($table.name)}[]> {
this.messageService.add(`${JsNameConverter($table.name)}Service: get data`);
return this.http.get<${JsNameConverter($table.name)}[]>(`\${serviceBaseUrl}/v1/${table.name}s`).toPromise()
}
async get${JsNameConverter($table.name)}(id: number): Promise<${JsNameConverter($table.name)}> {
this.messageService.add(`${JsNameConverter($table.name)}Service: get data for \${id}`);
return this.http.get<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}s/\${id}`).toPromise()
}
async post${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: post data for \${itemStr}`);
return this.http.post<${JsNameConverter($table.name)}>(`\${serviceBaseUrl}/v1/${table.name}s`, item).toPromise()
}
}
#end for

View File

@ -1,9 +1,18 @@
// -----------------------------------------
// THIS FILE HAS BEEN GENERATED
// DO NOT EDIT MANUALLY
// -----------------------------------------
export interface Account {
id: number
description: string
}
export interface Tenant {
id: number
salutation: string
firstname: string
lastname: string
@ -19,6 +28,7 @@ export interface Tenant {
}
export interface Premise {
id: number
description: string
street: string
zip: string
@ -26,6 +36,7 @@ export interface Premise {
}
export interface Flat {
id: number
description: string
premise: number
area: number
@ -33,6 +44,7 @@ export interface Flat {
}
export interface OverheadAdvance {
id: number
description: string
amount: number
startdate: string
@ -40,21 +52,25 @@ export interface OverheadAdvance {
}
export interface OverheadAdvanceFlatMapping {
id: number
overhead_advance: number
flat: number
}
export interface Parking {
id: number
description: string
premise: number
}
export interface CommercialPremise {
id: number
description: string
premise: number
}
export interface Tenancy {
id: number
description: string
tenant: number
flat: number
@ -65,6 +81,7 @@ export interface Tenancy {
}
export interface Fee {
id: number
description: string
amount: number
fee_type: string
@ -73,11 +90,13 @@ export interface Fee {
}
export interface TenancyFeeMapping {
id: number
tenancy: number
fee: number
}
export interface AccountEntry {
id: number
description: string
account: number
created_at: string

View File

@ -1,7 +1,10 @@
$GENERATED_TS_COMMENT
#from generateHelper import JsNameConverter
#for $table in $tables
export interface $JsNameConverter($table.name) {
id: number
#for $column in $table.columns
${column.name}: ${column.jstype}
#end for

View File

@ -0,0 +1,40 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{flat?.description}} {{flat?.flat_no}} {{premise?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{flat?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveFlat()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="flat.description"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Haus</mat-label>
<mat-select [(ngModel)]="flat.premise" name="premise">
<mat-option *ngFor="let p of premises" [value]="p.id">{{p.description}}</mat-option>
</mat-select>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Fläche</mat-label>
<input matInput name="area" [(ngModel)]="flat.area"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Wohnungsnummer</mat-label>
<input matInput name="flat_no" [(ngModel)]="flat.flat_no"/>
</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 { FlatDetailsComponent } from './flat-details.component';
describe('FlatDetailsComponent', () => {
let component: FlatDetailsComponent;
let fixture: ComponentFixture<FlatDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FlatDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FlatDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,83 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { FlatService, PremiseService } from '../data-object-service';
import { Flat, Premise } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-flat-details',
templateUrl: './flat-details.component.html',
styleUrls: ['./flat-details.component.css']
})
export class FlatDetailsComponent implements OnInit {
flat: Flat = {
id: 0,
description: '',
premise: 0,
area: 0.0,
flat_no: 0
}
premise: Premise = {
id: 0,
description: '',
street: '',
zip: '',
city: ''
}
premises: Premise[]
@ViewChild('submitButton') submitButton: MatButton
constructor(
private flatService: FlatService,
private premiseService: PremiseService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getFlat(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) {
this.flat = await this.flatService.getFlat(id)
this.premise = await this.premiseService.getPremise(this.flat.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 saveFlat() {
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.router.navigate(['/flats'])
}
ngOnInit(): void {
this.getPremises()
this.getFlat()
}
}

View File

@ -0,0 +1,25 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
Meine Büros
</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="premise">
<th mat-header-cell *matHeaderCellDef>Haus</th>
<td mat-cell *matCellDef="let element">{{element.premise}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/commercialunit/', 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 { MyCommercialUnitsComponent } from './my-commercial-units.component';
describe('MyCommercialUnitsComponent', () => {
let component: MyCommercialUnitsComponent;
let fixture: ComponentFixture<MyCommercialUnitsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyCommercialUnitsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyCommercialUnitsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
import { CommercialPremiseService } from '../data-object-service';
import { CommercialPremise } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table'
@Component({
selector: 'app-my-commercial-units',
templateUrl: './my-commercial-units.component.html',
styleUrls: ['./my-commercial-units.component.css']
})
export class MyCommercialUnitsComponent implements OnInit {
commercialPremises: CommercialPremise[]
dataSource: MatTableDataSource<CommercialPremise>
displayedColumns: string[] = ["description", "premise"]
constructor(private commercialPremiseService: CommercialPremiseService, private messageService: MessageService) { }
async getCommercialPremises(): Promise<void> {
try {
this.messageService.add("Trying to load commercialPremises")
this.commercialPremises = await this.commercialPremiseService.getCommercialPremises()
this.messageService.add("CommercialPremises loaded")
this.dataSource = new MatTableDataSource<CommercialPremise>(this.commercialPremises)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
ngOnInit(): void {
this.messageService.add("MyCommercialUnitsComponent.ngOnInit")
this.getCommercialPremises()
}
}

View File

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

View File

@ -0,0 +1,35 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
<span>Meine Wohnungen</span>
<span class="spacer"></span>
<a mat-button routerLink="/flat">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.flat.description}}</td>
</ng-container>
<ng-container matColumnDef="premise">
<th mat-header-cell *matHeaderCellDef>Haus</th>
<td mat-cell *matCellDef="let element">{{element.premise.description}}</td>
</ng-container>
<ng-container matColumnDef="area">
<th mat-header-cell *matHeaderCellDef>Wohnfläche</th>
<td mat-cell *matCellDef="let element">{{element.flat.area}}</td>
</ng-container>
<ng-container matColumnDef="flat_no">
<th mat-header-cell *matHeaderCellDef>Wohnungsnummer</th>
<td mat-cell *matCellDef="let element">{{element.flat.flat_no}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/flat/', 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 { MyFlatsComponent } from './my-flats.component';
describe('MyFlatsComponent', () => {
let component: MyFlatsComponent;
let fixture: ComponentFixture<MyFlatsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyFlatsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyFlatsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,65 @@
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
import { FlatService, PremiseService } from '../data-object-service';
import { Flat, Premise } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table'
interface DN_Flat {
flat: Flat
premise: Premise
}
@Component({
selector: 'app-my-flats',
templateUrl: './my-flats.component.html',
styleUrls: ['./my-flats.component.css']
})
export class MyFlatsComponent implements OnInit {
flats: Flat[]
premises: Premise[]
dnFlats: DN_Flat[]
dataSource: MatTableDataSource<DN_Flat>
displayedColumns: string[] = ["description", "premise", "area", "flat_no"]
constructor(
private flatService: FlatService,
private premiseService: PremiseService,
private messageService: MessageService
) { }
async getFlats(): Promise<void> {
try {
this.messageService.add("Trying to load flats")
this.flats = await this.flatService.getFlats()
this.messageService.add(`Flats loaded: ${ JSON.stringify(this.flats, undefined, 4) }`)
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add(`Premises loaded: ${ JSON.stringify(this.premises, undefined, 4) }`)
const premisesDict = new Map<number, Premise>()
for (let p of this.premises) {
this.messageService.add(`p2pd: ${p.id}`)
premisesDict.set(p.id, p)
}
this.messageService.add(`premisesDict: ${ JSON.stringify(premisesDict, undefined, 4) }`)
for (let f of this.flats) {
this.dnFlats.push({
flat: f,
premise: premisesDict.get(f.premise)
})
}
this.messageService.add(`dnFlats: { JSON.stringify(this.dnFlats, undefined, 4) }`)
this.dataSource = new MatTableDataSource<DN_Flat>(this.dnFlats)
} catch (err) {
this.messageService.add(`Error in getFlats: ${ JSON.stringify(err, undefined, 4) }`)
}
}
ngOnInit(): void {
this.messageService.add("MyFlatsComponent.ngOnInit")
this.getFlats()
}
}

View File

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

View File

@ -0,0 +1,25 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
Meine Garagen
</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="premise">
<th mat-header-cell *matHeaderCellDef>Haus</th>
<td mat-cell *matCellDef="let element">{{element.premise}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/parking/', 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 { MyParkingsComponent } from './my-parkings.component';
describe('MyParkingsComponent', () => {
let component: MyParkingsComponent;
let fixture: ComponentFixture<MyParkingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyParkingsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyParkingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
import { ParkingService } from '../data-object-service';
import { Parking } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table'
@Component({
selector: 'app-my-parkings',
templateUrl: './my-parkings.component.html',
styleUrls: ['./my-parkings.component.css']
})
export class MyParkingsComponent implements OnInit {
parkings: Parking[]
dataSource: MatTableDataSource<Parking>
displayedColumns: string[] = ["description", "premise"]
constructor(private parkingService: ParkingService, private messageService: MessageService) { }
async getParkings(): Promise<void> {
try {
this.messageService.add("Trying to load parkings")
this.parkings = await this.parkingService.getParkings()
this.messageService.add("Parkings loaded")
this.dataSource = new MatTableDataSource<Parking>(this.parkings)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
ngOnInit(): void {
this.messageService.add("MyParkingsComponent.ngOnInit")
this.getParkings()
}
}

View File

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

View File

@ -0,0 +1,35 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
<span>Meine Häuser</span>
<span class="spacer"></span>
<a mat-button routerLink="/premise">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="street">
<th mat-header-cell *matHeaderCellDef>Strasse</th>
<td mat-cell *matCellDef="let element">{{element.street}}</td>
</ng-container>
<ng-container matColumnDef="zip">
<th mat-header-cell *matHeaderCellDef>PLZ</th>
<td mat-cell *matCellDef="let element">{{element.zip}}</td>
</ng-container>
<ng-container matColumnDef="city">
<th mat-header-cell *matHeaderCellDef>Ort</th>
<td mat-cell *matCellDef="let element">{{element.city}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/premise/', 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 { MyPremisesComponent } from './my-premises.component';
describe('MyPremisesComponent', () => {
let component: MyPremisesComponent;
let fixture: ComponentFixture<MyPremisesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyPremisesComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyPremisesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { PremiseService } from '../data-object-service';
import { MessageService } from '../message.service';
import { Premise } from '../data-objects'
@Component({
selector: 'app-my-premises',
templateUrl: './my-premises.component.html',
styleUrls: ['./my-premises.component.css']
})
export class MyPremisesComponent implements OnInit {
premises: Premise[]
dataSource: MatTableDataSource<Premise>
displayedColumns: string[] = [ "description", "street", "zip", "city" ]
constructor(private premiseService: PremiseService, private messageService: MessageService) { }
async getPremises(): Promise<void> {
try {
this.messageService.add("Trying to load premises")
this.premises = await this.premiseService.getPremises()
this.messageService.add("Premises loaded")
this.dataSource = new MatTableDataSource<Premise>(this.premises)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
ngOnInit(): void {
this.messageService.add("MyPremisesComponent.ngOnInit")
this.getPremises()
}
}

View File

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

View File

@ -0,0 +1,31 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
<span>Meine Mieter/innen</span>
<span class="spacer"></span>
<a mat-button routerLink="/tenant">Neu anlegen</a>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div>
<table mat-table [dataSource]="dataSource" #zftable>
<ng-container matColumnDef="lastname">
<th mat-header-cell *matHeaderCellDef>Nachname</th>
<td mat-cell *matCellDef="let element">{{element.lastname}}</td>
</ng-container>
<ng-container matColumnDef="firstname">
<th mat-header-cell *matHeaderCellDef>Vorname</th>
<td mat-cell *matCellDef="let element">{{element.firstname}}</td>
</ng-container>
<ng-container matColumnDef="address1">
<th mat-header-cell *matHeaderCellDef>Adresse 1</th>
<td mat-cell *matCellDef="let element">{{element.address1}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/tenant/', 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 { MyTenantsComponent } from './my-tenants.component';
describe('MyTenantsComponent', () => {
let component: MyTenantsComponent;
let fixture: ComponentFixture<MyTenantsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyTenantsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MyTenantsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
import { TenantService } from '../data-object-service';
import { Tenant } from '../data-objects';
import { MatTableDataSource } from '@angular/material/table'
@Component({
selector: 'app-my-tenants',
templateUrl: './my-tenants.component.html',
styleUrls: ['./my-tenants.component.css']
})
export class MyTenantsComponent implements OnInit {
tenants: Tenant[]
dataSource: MatTableDataSource<Tenant>
displayedColumns: string[] = ["lastname", "firstname", "address1"]
constructor(private tenantService: TenantService, private messageService: MessageService) { }
async getTenants(): Promise<void> {
try {
this.messageService.add("Trying to load tenants")
this.tenants = await this.tenantService.getTenants()
this.messageService.add("Tenants loaded")
this.dataSource = new MatTableDataSource<Tenant>(this.tenants)
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
ngOnInit(): void {
this.messageService.add("MyTenantsComponent.ngOnInit")
this.getTenants()
}
}

View File

@ -5,7 +5,11 @@
[opened]="(isHandset$ | async) === false">
<mat-toolbar>Menu</mat-toolbar>
<mat-nav-list>
<a mat-list-item href="/test">Mein Test</a>
<a mat-list-item href="/premises">Meine Häuser</a>
<a mat-list-item href="/flats">Meine Wohnungen</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="/tenants">Meine Mieter/innen</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>

View File

@ -0,0 +1,39 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{premise?.description}}
</mat-card-title>
<mat-card-subtitle>
ID: {{premise?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="savePremise()">
<div>
<mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label>
<input matInput name="description" [(ngModel)]="premise.description"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Strasse</mat-label>
<input matInput name="street" [(ngModel)]="premise.street"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>PLZ</mat-label>
<input matInput name="zip" [(ngModel)]="premise.zip"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Ort</mat-label>
<input matInput name="city" [(ngModel)]="premise.city"/>
</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 { PremiseDetailsComponent } from './premise-details.component';
describe('PremiseDetailsComponent', () => {
let component: PremiseDetailsComponent;
let fixture: ComponentFixture<PremiseDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PremiseDetailsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PremiseDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,62 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ActivatedRoute, Router } from '@angular/router';
import { PremiseService } from '../data-object-service';
import { Premise } from '../data-objects';
import { MessageService } from '../message.service';
@Component({
selector: 'app-premise-details',
templateUrl: './premise-details.component.html',
styleUrls: ['./premise-details.component.css']
})
export class PremiseDetailsComponent implements OnInit {
@ViewChild('submitButton') submitButton: MatButton
premise: Premise = {
id: 0,
description: '',
street: '',
zip: '',
city: ''
}
constructor(
private premiseService: PremiseService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getPremise(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) {
this.premise = await this.premiseService.getPremise(id)
}
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async savePremise() {
this.submitButton.disabled = true
this.messageService.add("savePremise")
this.messageService.add(JSON.stringify(this.premise, undefined, 4))
if (this.premise.id == 0) {
this.messageService.add("about to insert new premise")
this.premise = await this.premiseService.postPremise(this.premise)
this.messageService.add(`Successfully added premises with id ${this.premise.id}`)
} else {
this.messageService.add("about to update existing premise")
}
this.router.navigate(['/premises'])
// this.submitButton.disabled = false
}
ngOnInit(): void {
this.getPremise()
}
}

View File

@ -0,0 +1,78 @@
<section class="mat-typography">
<mat-card class="defaultCard">
<mat-card-header>
<mat-card-title>
{{tenant?.firstname}} {{tenant?.lastname}}
</mat-card-title>
<mat-card-subtitle>
ID: {{tenant?.id}}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<form (ngSubmit)="saveTenant()">
<div>
<mat-form-field appearance="outline">
<mat-label>Anrede</mat-label>
<input matInput name="salutation" [(ngModel)]="tenant.salutation"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Vorname</mat-label>
<input matInput name="firstname" [(ngModel)]="tenant.firstname"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Nachname</mat-label>
<input matInput name="lastname" [(ngModel)]="tenant.lastname"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Adresse 1 (Straße)</mat-label>
<input matInput name="address" [(ngModel)]="tenant.address1"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Adresse 2</mat-label>
<input matInput name="address2" [(ngModel)]="tenant.address2"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Adresse 3</mat-label>
<input matInput name="address3" [(ngModel)]="tenant.address3"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>PLZ</mat-label>
<input matInput name="zip" [(ngModel)]="tenant.zip"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Ort</mat-label>
<input matInput name="city" [(ngModel)]="tenant.city"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Telefon 1</mat-label>
<input matInput name="phone1" [(ngModel)]="tenant.phone1"/>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Telefon 2</mat-label>
<input matInput name="phone2" [(ngModel)]="tenant.phone2"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>IBAN</mat-label>
<input matInput name="iban" [(ngModel)]="tenant.iban"/>
</mat-form-field>
</div><div>
<mat-form-field appearance="outline">
<mat-label>Account ID</mat-label>
<input matInput name="account_id" [readonly]="true" [ngModel]="account.id"/>
</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>
</mat-card-content>
</mat-card>
</section>

View File

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

View File

@ -0,0 +1,82 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AccountService, TenantService } from '../data-object-service';
import { Account, Tenant } from '../data-objects';
import { MessageService } from '../message.service';
import { MatButton } from '@angular/material/button';
@Component({
selector: 'app-tenant-details',
templateUrl: './tenant-details.component.html',
styleUrls: ['./tenant-details.component.css']
})
export class TenantDetailsComponent implements OnInit {
tenant: Tenant = {
id: 0,
salutation: '',
firstname: '',
lastname: '',
address1: '',
address2: '',
address3: '',
zip: '',
city: '',
phone1: '',
phone2: '',
iban: '',
account: 0
}
account: Account = {
id: 0,
description: ''
}
@ViewChild('submitButton') submitButton: MatButton
constructor(
private tenantService: TenantService,
private accountService: AccountService,
private messageService: MessageService,
private route: ActivatedRoute,
private router: Router
) { }
async getTenant(): Promise<void> {
try {
const id = +this.route.snapshot.paramMap.get('id')
if (id != 0) {
this.tenant = await this.tenantService.getTenant(id)
this.account = await this.accountService.getAccount(this.tenant.account)
}
} catch (err) {
this.messageService.add(JSON.stringify(err, undefined, 4))
}
}
async saveTenant() {
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")
}
this.router.navigate(['/tenants'])
}
ngOnInit(): void {
this.getTenant()
}
}