Add float & monetary custom field types
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
<div [class.col-md-9]="horizontal">
|
||||
<div class="input-group" [class.is-invalid]="error">
|
||||
<input #inputField type="number" class="form-control" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [class.is-invalid]="error" [disabled]="disabled">
|
||||
<input #inputField type="number" class="form-control" [step]="step" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [class.is-invalid]="error" [disabled]="disabled">
|
||||
<button *ngIf="showAdd" class="btn btn-outline-secondary" type="button" id="button-addon1" (click)="nextAsn()" [disabled]="disabled">+1</button>
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
|
||||
@@ -46,4 +46,18 @@ describe('NumberComponent', () => {
|
||||
component.nextAsn()
|
||||
expect(component.value).toEqual(1002)
|
||||
})
|
||||
|
||||
it('should support float & monetary values', () => {
|
||||
component.writeValue(11.13)
|
||||
expect(component.value).toEqual(11)
|
||||
component.step = 0.01
|
||||
component.writeValue(11.1)
|
||||
expect(component.value).toEqual('11.10')
|
||||
component.step = 0.1
|
||||
component.writeValue(12.3456)
|
||||
expect(component.value).toEqual(12.3456)
|
||||
// float (step = .1) doesnt force 2 decimals
|
||||
component.writeValue(11.1)
|
||||
expect(component.value).toEqual(11.1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,9 @@ export class NumberComponent extends AbstractInputComponent<number> {
|
||||
@Input()
|
||||
showAdd: boolean = true
|
||||
|
||||
@Input()
|
||||
step: number = 1
|
||||
|
||||
constructor(private documentService: DocumentService) {
|
||||
super()
|
||||
}
|
||||
@@ -32,4 +35,10 @@ export class NumberComponent extends AbstractInputComponent<number> {
|
||||
this.onChange(this.value)
|
||||
})
|
||||
}
|
||||
|
||||
writeValue(newValue: any): void {
|
||||
if (this.step === 1) newValue = parseInt(newValue, 10)
|
||||
if (this.step === 0.01) newValue = parseFloat(newValue).toFixed(2)
|
||||
super.writeValue(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,8 @@
|
||||
<pngx-input-text formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.String" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-text>
|
||||
<pngx-input-date formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Date" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-date>
|
||||
<pngx-input-number formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Integer" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [showAdd]="false"></pngx-input-number>
|
||||
<pngx-input-number formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Float" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [showAdd]="false" [step]=".1"></pngx-input-number>
|
||||
<pngx-input-number formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Monetary" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [showAdd]="false" [step]=".01"></pngx-input-number>
|
||||
<pngx-input-check formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Boolean" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-check>
|
||||
<pngx-input-url formControlName="value" *ngIf="getCustomFieldFromInstance(fieldInstance)?.data_type === PaperlessCustomFieldDataType.Url" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-url>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,8 @@ export enum PaperlessCustomFieldDataType {
|
||||
Date = 'date',
|
||||
Boolean = 'boolean',
|
||||
Integer = 'integer',
|
||||
Float = 'float',
|
||||
Monetary = 'monetary',
|
||||
}
|
||||
|
||||
export const DATA_TYPE_LABELS = [
|
||||
@@ -19,8 +21,16 @@ export const DATA_TYPE_LABELS = [
|
||||
},
|
||||
{
|
||||
id: PaperlessCustomFieldDataType.Integer,
|
||||
name: $localize`Integer`,
|
||||
},
|
||||
{
|
||||
id: PaperlessCustomFieldDataType.Float,
|
||||
name: $localize`Number`,
|
||||
},
|
||||
{
|
||||
id: PaperlessCustomFieldDataType.Monetary,
|
||||
name: $localize`Monetary`,
|
||||
},
|
||||
{
|
||||
id: PaperlessCustomFieldDataType.String,
|
||||
name: $localize`String`,
|
||||
|
||||
@@ -43,6 +43,8 @@ class Migration(migrations.Migration):
|
||||
("date", "Date"),
|
||||
("boolean", "Boolean"),
|
||||
("integer", "Integer"),
|
||||
("float", "Float"),
|
||||
("monetary", "Monetary"),
|
||||
],
|
||||
editable=False,
|
||||
max_length=50,
|
||||
@@ -82,6 +84,11 @@ class Migration(migrations.Migration):
|
||||
("value_url", models.URLField(null=True)),
|
||||
("value_date", models.DateField(null=True)),
|
||||
("value_int", models.IntegerField(null=True)),
|
||||
("value_float", models.FloatField(null=True)),
|
||||
(
|
||||
"value_monetary",
|
||||
models.DecimalField(decimal_places=2, max_digits=12, null=True),
|
||||
),
|
||||
(
|
||||
"document",
|
||||
models.ForeignKey(
|
||||
|
||||
@@ -888,6 +888,8 @@ class CustomField(models.Model):
|
||||
DATE = ("date", _("Date"))
|
||||
BOOL = ("boolean"), _("Boolean")
|
||||
INT = ("integer", _("Integer"))
|
||||
FLOAT = ("float", _("Float"))
|
||||
MONETARY = ("monetary", _("Monetary"))
|
||||
|
||||
created = models.DateTimeField(
|
||||
_("created"),
|
||||
@@ -962,6 +964,10 @@ class CustomFieldInstance(models.Model):
|
||||
|
||||
value_int = models.IntegerField(null=True)
|
||||
|
||||
value_float = models.FloatField(null=True)
|
||||
|
||||
value_monetary = models.DecimalField(null=True, decimal_places=2, max_digits=12)
|
||||
|
||||
class Meta:
|
||||
ordering = ("created",)
|
||||
verbose_name = _("custom field instance")
|
||||
@@ -992,6 +998,10 @@ class CustomFieldInstance(models.Model):
|
||||
return self.value_bool
|
||||
elif self.field.data_type == CustomField.FieldDataType.INT:
|
||||
return self.value_int
|
||||
elif self.field.data_type == CustomField.FieldDataType.FLOAT:
|
||||
return self.value_float
|
||||
elif self.field.data_type == CustomField.FieldDataType.MONETARY:
|
||||
return self.value_monetary
|
||||
raise NotImplementedError(self.field.data_type)
|
||||
|
||||
|
||||
|
||||
@@ -437,6 +437,8 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
|
||||
CustomField.FieldDataType.DATE: "value_date",
|
||||
CustomField.FieldDataType.BOOL: "value_bool",
|
||||
CustomField.FieldDataType.INT: "value_int",
|
||||
CustomField.FieldDataType.FLOAT: "value_float",
|
||||
CustomField.FieldDataType.MONETARY: "value_monetary",
|
||||
}
|
||||
# An instance is attached to a document
|
||||
document: Document = validated_data["document"]
|
||||
|
||||
@@ -34,6 +34,7 @@ class TestCustomField(DirectoriesMixin, APITestCase):
|
||||
("date", "Invoiced Date"),
|
||||
("integer", "Invoice #"),
|
||||
("boolean", "Is Active"),
|
||||
("float", "Total Paid"),
|
||||
]:
|
||||
resp = self.client.post(
|
||||
self.ENDPOINT,
|
||||
@@ -87,6 +88,14 @@ class TestCustomField(DirectoriesMixin, APITestCase):
|
||||
name="Test Custom Field Url",
|
||||
data_type=CustomField.FieldDataType.URL,
|
||||
)
|
||||
custom_field_float = CustomField.objects.create(
|
||||
name="Test Custom Field Float",
|
||||
data_type=CustomField.FieldDataType.FLOAT,
|
||||
)
|
||||
custom_field_monetary = CustomField.objects.create(
|
||||
name="Test Custom Field Monetary",
|
||||
data_type=CustomField.FieldDataType.MONETARY,
|
||||
)
|
||||
|
||||
date_value = date.today()
|
||||
|
||||
@@ -114,6 +123,14 @@ class TestCustomField(DirectoriesMixin, APITestCase):
|
||||
"field": custom_field_url.id,
|
||||
"value": "https://example.com",
|
||||
},
|
||||
{
|
||||
"field": custom_field_float.id,
|
||||
"value": 12.3456,
|
||||
},
|
||||
{
|
||||
"field": custom_field_monetary.id,
|
||||
"value": 11.10,
|
||||
},
|
||||
],
|
||||
},
|
||||
format="json",
|
||||
@@ -131,11 +148,13 @@ class TestCustomField(DirectoriesMixin, APITestCase):
|
||||
{"field": custom_field_int.id, "value": 3},
|
||||
{"field": custom_field_boolean.id, "value": True},
|
||||
{"field": custom_field_url.id, "value": "https://example.com"},
|
||||
{"field": custom_field_float.id, "value": 12.3456},
|
||||
{"field": custom_field_monetary.id, "value": 11.10},
|
||||
],
|
||||
)
|
||||
|
||||
doc.refresh_from_db()
|
||||
self.assertEqual(len(doc.custom_fields.all()), 5)
|
||||
self.assertEqual(len(doc.custom_fields.all()), 7)
|
||||
|
||||
def test_change_custom_field_instance_value(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user