Merge branch 'master' of git.dorfsvald.net:ewandor/cht-lawfirm

This commit is contained in:
2023-03-14 19:00:18 +01:00
8 changed files with 112 additions and 74 deletions

View File

@@ -2,7 +2,7 @@ import datetime
import os import os
import base64 import base64
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, FileResponse from fastapi.responses import HTMLResponse, FileResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
@@ -61,47 +61,47 @@ print_router = APIRouter()
templates = Jinja2Templates(directory=str(BASE_PATH / "templates")) templates = Jinja2Templates(directory=str(BASE_PATH / "templates"))
async def render_print(host, contract): async def render_print(root_url, contract):
template = templates.get_template("print.html") template = templates.get_template("print.html")
return template.render({ return template.render({
"contract": contract, "contract": contract,
"static_host": host "root_url": root_url
}) })
async def render_css(host, contract): async def render_css(root_url, contract):
template = templates.get_template("styles.css") template = templates.get_template("styles.css")
return template.render({ return template.render({
"contract": contract, "contract": contract,
"static_host": host "root_url": root_url
}) })
@print_router.get("/preview/draft/{draft_id}", response_class=HTMLResponse) @print_router.get("/preview/draft/{draft_id}", response_class=HTMLResponse)
async def preview_draft(draft_id: str) -> str: async def preview_draft(draft_id: str, request: Request) -> str:
draft = await build_model(await ContractDraft.get(draft_id)) draft = await build_model(await ContractDraft.get(draft_id))
return await render_print('localhost', draft) return await render_print(f'{request.url.scheme}://{request.url.hostname}', draft)
@print_router.get("/preview/signature/{signature_id}", response_class=HTMLResponse) @print_router.get("/preview/signature/{signature_id}", response_class=HTMLResponse)
async def preview_contract_by_signature(signature_id: str) -> str: async def preview_contract_by_signature(signature_id: str, request: Request) -> str:
contract = await Contract.find_by_signature_id(signature_id) contract = await Contract.find_by_signature_id(signature_id)
for p in contract.parties: for p in contract.parties:
if p.signature_affixed: if p.signature_affixed:
p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png') p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png')
return await render_print('localhost', contract) return await render_print(f'{request.url.scheme}://{request.url.hostname}', contract)
@print_router.get("/preview/{contract_id}", response_class=HTMLResponse) @print_router.get("/preview/{contract_id}", response_class=HTMLResponse)
async def preview_contract(contract_id: str) -> str: async def preview_contract(contract_id: str, request: Request) -> str:
contract = await Contract.get(contract_id) contract = await Contract.get(contract_id)
for p in contract.parties: for p in contract.parties:
if p.signature_affixed: if p.signature_affixed:
p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png') p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png')
return await render_print('localhost', contract) return await render_print(f'{request.url.scheme}://{request.url.hostname}', contract)
@print_router.get("/pdf/{contract_id}", response_class=FileResponse) @print_router.get("/pdf/{contract_id}", response_class=FileResponse)
@@ -118,8 +118,8 @@ async def create_pdf(contract_id: str) -> str:
# os.remove(signature_path) # os.remove(signature_path)
font_config = FontConfiguration() font_config = FontConfiguration()
html = HTML(string=await render_print('nginx', contract)) html = HTML(string=await render_print('http://nginx', contract))
css = CSS(string=await render_css('nginx', contract), font_config=font_config) css = CSS(string=await render_css('http://nginx', contract), font_config=font_config)
html.write_pdf(contract_path, stylesheets=[css], font_config=font_config) html.write_pdf(contract_path, stylesheets=[css], font_config=font_config)
update_query = {"$set": { update_query = {"$set": {

View File

@@ -8,7 +8,7 @@
<div class="frontpage"> <div class="frontpage">
<div id="front-page-header"> <div id="front-page-header">
<table><tr> <table><tr>
<td><img id="top-logo" src="http://{{ static_host }}/assets/logotransparent.png" alt="Cooper, Hillman & Toshi logo"></td> <td><img id="top-logo" src="{{ root_url }}/assets/logotransparent.png" alt="Cooper, Hillman & Toshi logo"></td>
<td id="office-info">Cooper, Hillman & Toshi LLP<br />6834 Innocence Boulevard<br />LOS SANTOS - SA<br /><a href="#">consulting@cht.law.com</a></td> <td id="office-info">Cooper, Hillman & Toshi LLP<br />6834 Innocence Boulevard<br />LOS SANTOS - SA<br /><a href="#">consulting@cht.law.com</a></td>
</tr></table> </tr></table>
<h1>{{ contract.title|upper }}</h1> <h1>{{ contract.title|upper }}</h1>
@@ -27,7 +27,7 @@
{{ party.entity.entity_data.title }} soci&eacute;t&eacute; de {{ party.entity.entity_data.activity }} enregistr&eacute;e aupr&egrave;s du gouvernement de San Andreas et domicili&eacute;e au {{ party.entity.address }}{% if party.representative %}, repr&eacute;sent&eacute;e par {{ party.representative.entity_data.firstname }} {{ party.representative.entity_data.middlenames }} {{ party.representative.entity_data.lastname }}{% endif %} {{ party.entity.entity_data.title }} soci&eacute;t&eacute; de {{ party.entity.entity_data.activity }} enregistr&eacute;e aupr&egrave;s du gouvernement de San Andreas et domicili&eacute;e au {{ party.entity.address }}{% if party.representative %}, repr&eacute;sent&eacute;e par {{ party.representative.entity_data.firstname }} {{ party.representative.entity_data.middlenames }} {{ party.representative.entity_data.lastname }}{% endif %}
{% elif party.entity.entity_data.type == "individual" %} {% elif party.entity.entity_data.type == "individual" %}
{{ party.entity.entity_data.firstname }} {{ party.entity.entity_data.middlenames }} {{ party.entity.entity_data.lastname }} {{ party.entity.entity_data.firstname }} {{ party.entity.entity_data.middlenames }} {{ party.entity.entity_data.lastname }}
{% if party.entity.entity_data.day_of_birth %} n&eacute; le {{ party.entity.entity_data.day_of_birth.strftime('%d/%m/%Y') }} {% if true %} &agrave; {{ party.entity.entity_data.place_of_birth }}{% endif %},{% endif %} {% if party.entity.entity_data.day_of_birth %} n&eacute; le {{ party.entity.entity_data.day_of_birth.strftime('%d/%m/%Y') }} {% if party.entity.entity_data.place_of_birth %} &agrave; {{ party.entity.entity_data.place_of_birth }}{% endif %},{% endif %}
{% if party.entity.address %} r&eacute;sidant &agrave; {{ party.entity.address }}, {% endif %} {% if party.entity.address %} r&eacute;sidant &agrave; {{ party.entity.address }}, {% endif %}
{% elif party.entity.entity_data.type == "institution" %} {% elif party.entity.entity_data.type == "institution" %}

View File

@@ -1,24 +1,24 @@
@font-face { @font-face {
font-family: 'Century Schoolbook'; font-family: 'Century Schoolbook';
src: url('http://{{ static_host }}/assets/century-schoolbook/CenturySchoolbookRegular.ttf'); src: url('{{ root_url }}/assets/century-schoolbook/CenturySchoolbookRegular.ttf');
} }
@font-face { @font-face {
font-family: "Century Schoolbook"; font-family: "Century Schoolbook";
src: url("http://{{ static_host }}/assets/century-schoolbook/CenturySchoolbookBold.ttf"); src: url("{{ root_url }}/assets/century-schoolbook/CenturySchoolbookBold.ttf");
font-weight: bold; font-weight: bold;
} }
@font-face { @font-face {
font-family: "Century Schoolbook"; font-family: "Century Schoolbook";
src: url("http://{{ static_host }}/assets/century-schoolbook/CenturySchoolbookItalic.ttf"); src: url("{{ root_url }}/assets/century-schoolbook/CenturySchoolbookItalic.ttf");
font-style: italic; font-style: italic;
} }
@font-face { @font-face {
font-family: "Century Schoolbook"; font-family: "Century Schoolbook";
src: url("http://{{ static_host }}/assets/century-schoolbook/CenturySchoolbookBoldItalic.ttf"); src: url("{{ root_url }}/assets/century-schoolbook/CenturySchoolbookBoldItalic.ttf");
font-weight: bold; font-weight: bold;
font-style: italic; font-style: italic;
} }
@@ -31,7 +31,7 @@
content: "© Cooper, Hillman & Toshi LLC - {{ contract.name }} - Page " counter(page) "/" counter(pages); content: "© Cooper, Hillman & Toshi LLC - {{ contract.name }} - Page " counter(page) "/" counter(pages);
font-size: 0.8em; font-size: 0.8em;
} }
background: url('http://{{ static_host }}/assets/watermark.png') no-repeat; background: url('{{ root_url }}/assets/watermark.png') no-repeat;
background-size:contain; background-size:contain;
} }

View File

@@ -15,13 +15,17 @@ class ContractDraftRead(ContractDraft):
class ContractDraftCreate(Writer): class ContractDraftCreate(Writer):
name: str name: str = Field(title='Nom')
title: str title: str = Field(title='Titre')
parties: List[DraftParty] parties: List[DraftParty] = Field(title='Parties')
provisions: List[DraftProvision] provisions: List[DraftProvision] = Field(
props={"items-per-row": "1", "numbered": True},
title='Clauses'
)
variables: List[DictionaryEntry] = Field( variables: List[DictionaryEntry] = Field(
default=[], default=[],
format="dictionary", format="dictionary",
title='Variables'
) )
async def validate_foreign_key(self): async def validate_foreign_key(self):

View File

@@ -23,10 +23,9 @@ class Individual(EntityType):
props={"items-per-row": "4", "numbered": True}, props={"items-per-row": "4", "numbered": True},
title="Surnoms" title="Surnoms"
) )
day_of_birth: date = Field(title='Date de naissance') day_of_birth: date = Field(default=None, title='Date de naissance')
place_of_birth: str = Field(default="", title='Lieu de naissance') place_of_birth: str = Field(default="", title='Lieu de naissance')
@property @property
def label(self) -> str: def label(self) -> str:
if len(self.surnames) > 0: if len(self.surnames) > 0:

View File

@@ -97,7 +97,7 @@ export class DraftsNewComponent extends BaseDraftsComponent implements OnInit {
(resourceReceived)="this.onResourceReceived($event)" (resourceReceived)="this.onResourceReceived($event)"
> >
</base-card> </base-card>
<a class="btn btn-link" href="http://localhost/api/v1/contract/print/preview/draft/{{this.resource_id}}" target="_blank">Preview</a> <a class="btn btn-link" href="/api/v1/contract/print/preview/draft/{{this.resource_id}}" target="_blank">Preview</a>
<ng-container *ngIf="this.isReadyForPublication;"> <ng-container *ngIf="this.isReadyForPublication;">
<formly-form [fields]="newContractFormfields" [form]="newContractForm" [model]="newContractModel"></formly-form> <formly-form [fields]="newContractFormfields" [form]="newContractForm" [model]="newContractModel"></formly-form>
<button class="btn btn-success" (click)="publish()">Publish</button> <button class="btn btn-success" (click)="publish()">Publish</button>
@@ -105,18 +105,20 @@ export class DraftsNewComponent extends BaseDraftsComponent implements OnInit {
` `
}) })
export class DraftsCardComponent extends BaseDraftsComponent implements OnInit { export class DraftsCardComponent extends BaseDraftsComponent implements OnInit {
resource_id: string | null = null;templateModel: {} = {}; resource_id: string | null = null;
templateModel: {} = {};
isReadyForPublication = false; isReadyForPublication = false;
newContractFormfields: FormlyFieldConfig[] = []; newContractFormfields: FormlyFieldConfig[] = [];
newContractForm: FormGroup = new FormGroup({}); newContractForm: FormGroup = new FormGroup({});
newContractModel: any = { newContractModel: any = {
date: formatDate(new Date(),'YYYY-MM-dd', 'EN_US', 'CET'), date: new Date(),
location: "Los Santos, SA", location: "Los Santos, SA",
draft_id: null draft_id: null
} }
fieldJson = { fieldJson = {
type: "object", type: "object",
required: ["date", "location", "draft_id"],
properties: { properties: {
date: { date: {
type: "string", type: "string",

View File

@@ -1,7 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import { formatDate } from "@angular/common"; import { formatDate } from "@angular/common";
import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap'; import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { FieldType, FieldTypeConfig } from '@ngx-formly/core'; import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
@@ -15,12 +14,12 @@ import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors"> <div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
<formly-validation-message [field]="field"></formly-validation-message> <formly-validation-message [field]="field"></formly-validation-message>
</div> </div>
<input type="hidden"
[formControl]="formControl"
[formlyAttributes]="field"
/>
<div class="input-group" *ngIf="! this.field.props.readonly"> <div class="input-group" *ngIf="! this.field.props.readonly">
<input type="hidden" <button class="btn btn-outline-secondary" (click)="d.toggle()" type="button"><i-bs name="calendar-date-fill"></i-bs></button>
[formControl]="formControl"
[formlyAttributes]="field"
[class.is-invalid]="showError"
/>
<input <input
class="form-control" class="form-control"
placeholder="yyyy-mm-dd" placeholder="yyyy-mm-dd"
@@ -29,33 +28,40 @@ import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
(ngModelChange)="changeDatetime($event)" (ngModelChange)="changeDatetime($event)"
ngbDatepicker ngbDatepicker
#d="ngbDatepicker" #d="ngbDatepicker"
[class.is-invalid]="showError"
/> />
<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button"><i-bs name="calendar-date-fill"></i-bs></button>
</div> </div>
<div class="input-group" *ngIf="this.field.props.readonly"> <div class="input-group" *ngIf="this.field.props.readonly">
<input class="form-control" value="{{ this.datetime.toLocaleString() }}" disabled=""/> <input class="form-control" value="{{ this.datetime ? this.datetime.toLocaleString() : '' }}" disabled=""/>
</div> </div>
`, `,
}) })
export class DateTypeComponent extends FieldType<FieldTypeConfig> implements OnInit export class DateTypeComponent extends FieldType<FieldTypeConfig> implements OnInit
{ {
public date : NgbDateStruct; public date : NgbDateStruct | null = null;
public datetime : Date = new Date(); public datetime : Date | null = null;
constructor() { constructor() {
super(); super();
this.date = this.getDateStruct(new Date());
} }
ngOnInit() { ngOnInit() {
if (this.formControl.value === undefined) { if (this.formControl.value === undefined) {
this.changeDatetime({}); this.changeDatetime({});
} else {
this.datetime = new Date(this.formControl.value);
this.date = this.getDateStruct(this.datetime);
} }
this.formControl.valueChanges.subscribe(value => { this.formControl.valueChanges.subscribe(value => {
this.datetime = new Date(value) if (value) {
this.date = this.getDateStruct(this.datetime); this.datetime = new Date(value);
this.date = this.getDateStruct(this.datetime);
} else {
this.datetime = null;
this.date = null;
}
}) })
} }
@@ -68,12 +74,21 @@ export class DateTypeComponent extends FieldType<FieldTypeConfig> implements OnI
} }
changeDatetime(event: any) { changeDatetime(event: any) {
this.datetime.setFullYear(this.date.year) if (this.date) {
this.datetime.setMonth(this.date.month - 1) if (!this.datetime) {
this.datetime.setDate(this.date.day) this.datetime = new Date();
}
this.datetime.setFullYear(this.date.year)
this.datetime.setMonth(this.date.month - 1)
this.datetime.setDate(this.date.day)
this.formControl.setValue( this.formControl.setValue(
formatDate(this.datetime, 'YYYY-MM-dd', 'EN_US', 'CET') formatDate(this.datetime, 'YYYY-MM-dd', 'EN_US', 'CET')
) )
} else {
this.datetime = null;
this.date = null;
this.formControl.setValue('')
}
} }
} }

View File

@@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import { formatDate } from "@angular/common"; import { formatDate } from "@angular/common";
import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap'; import { NgbDateStruct, NgbTimeStruct } from '@ng-bootstrap/ng-bootstrap';
import { FieldType, FieldTypeConfig } from '@ngx-formly/core'; import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
@@ -12,12 +11,12 @@ import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
class="form-label">{{ props.label }} class="form-label">{{ props.label }}
<span *ngIf="props.required && props['hideRequiredMarker'] !== true" aria-hidden="true">*</span> <span *ngIf="props.required && props['hideRequiredMarker'] !== true" aria-hidden="true">*</span>
</label> </label>
<input type="hidden"
[formControl]="formControl"
[formlyAttributes]="field"
/>
<div class="input-group" *ngIf="! this.field.props.readonly"> <div class="input-group" *ngIf="! this.field.props.readonly">
<input type="hidden" <button class="btn btn-outline-secondary bi bi-calendar3" (click)="d.toggle()" type="button"></button>
[formControl]="formControl"
[formlyAttributes]="field"
[class.is-invalid]="showError"
/>
<input <input
class="form-control" class="form-control"
placeholder="yyyy-mm-dd" placeholder="yyyy-mm-dd"
@@ -26,8 +25,8 @@ import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
(ngModelChange)="changeDatetime($event)" (ngModelChange)="changeDatetime($event)"
ngbDatepicker ngbDatepicker
#d="ngbDatepicker" #d="ngbDatepicker"
[class.is-invalid]="showError"
/> />
<button class="btn btn-outline-secondary bi bi-calendar3" (click)="d.toggle()" type="button"></button>
<ngb-timepicker <ngb-timepicker
(ngModelChange)="changeDatetime($event)" (ngModelChange)="changeDatetime($event)"
[(ngModel)]="time" [(ngModel)]="time"
@@ -35,15 +34,15 @@ import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
</ngb-timepicker> </ngb-timepicker>
</div> </div>
<div class="input-group" *ngIf="this.field.props.readonly"> <div class="input-group" *ngIf="this.field.props.readonly">
<input class="form-control" value="{{ this.datetime.toLocaleString() }}" disabled=""/> <input class="form-control" value="{{ this.datetime ? this.datetime.toLocaleString() : '' }}" disabled=""/>
</div> </div>
`, `,
}) })
export class DatetimeTypeComponent extends FieldType<FieldTypeConfig> implements OnInit export class DatetimeTypeComponent extends FieldType<FieldTypeConfig> implements OnInit
{ {
public time : NgbTimeStruct; public time : NgbTimeStruct | null = null;
public date : NgbDateStruct; public date : NgbDateStruct | null = null;
public datetime : Date = new Date() public datetime : Date | null = null;
constructor() { constructor() {
@@ -55,12 +54,21 @@ export class DatetimeTypeComponent extends FieldType<FieldTypeConfig> implements
ngOnInit() { ngOnInit() {
if (this.formControl.value === undefined) { if (this.formControl.value === undefined) {
this.changeDatetime({}); this.changeDatetime({});
} else {
this.datetime = new Date(this.formControl.value);
this.date = this.getDateStruct(this.datetime);
} }
this.formControl.valueChanges.subscribe(value => { this.formControl.valueChanges.subscribe(value => {
this.datetime = new Date(value) if (value) {
this.date = this.getDateStruct(this.datetime); this.datetime = new Date(value);
this.time = this.getTimeStruct(this.datetime); this.date = this.getDateStruct(this.datetime);
this.time = this.getTimeStruct(this.datetime);
} else {
this.datetime = null;
this.date = null;
this.time = null;
}
}) })
} }
@@ -81,15 +89,25 @@ export class DatetimeTypeComponent extends FieldType<FieldTypeConfig> implements
} }
changeDatetime(event: any) { changeDatetime(event: any) {
this.datetime.setFullYear(this.date.year) if (this.date && this.time) {
this.datetime.setMonth(this.date.month - 1) if (!this.datetime) {
this.datetime.setDate(this.date.day) this.datetime = new Date();
this.datetime.setHours(this.time.hour) }
this.datetime.setMinutes(this.time.minute) this.datetime.setFullYear(this.date.year)
this.datetime.setSeconds(this.time.second) this.datetime.setMonth(this.date.month - 1)
this.datetime.setDate(this.date.day)
this.datetime.setHours(this.time.hour)
this.datetime.setMinutes(this.time.minute)
this.datetime.setSeconds(this.time.second)
this.formControl.setValue( this.formControl.setValue(
formatDate(this.datetime, 'YYYY-MM-ddTHH:mm:ss.SSS', 'EN_US', 'CET') formatDate(this.datetime, 'YYYY-MM-ddTHH:mm:ss.SSS', 'EN_US', 'CET')
) )
} else {
this.datetime = null;
this.date = null;
this.time = null;
this.formControl.setValue('')
}
} }
} }