</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">200</context>
+ <context context-type="linenumber">201</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">219</context>
+ <context context-type="linenumber">220</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">286</context>
+ <context context-type="linenumber">287</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">305</context>
+ <context context-type="linenumber">306</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">208</context>
+ <context context-type="linenumber">209</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">227</context>
+ <context context-type="linenumber">228</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">294</context>
+ <context context-type="linenumber">295</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">313</context>
+ <context context-type="linenumber">314</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">233</context>
+ <context context-type="linenumber">234</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">319</context>
+ <context context-type="linenumber">320</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">87</context>
+ <context context-type="linenumber">88</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">83</context>
+ <context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">129</context>
+ <context context-type="linenumber">130</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
<source>Assign owner</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">194</context>
+ <context context-type="linenumber">195</context>
</context-group>
</trans-unit>
<trans-unit id="1749184201773078639" datatype="html">
<source>Assign view permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">196</context>
+ <context context-type="linenumber">197</context>
</context-group>
</trans-unit>
<trans-unit id="1744964187586405039" datatype="html">
<source>Assign edit permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">215</context>
+ <context context-type="linenumber">216</context>
</context-group>
</trans-unit>
<trans-unit id="6236311670364192011" datatype="html">
<source>Remove tags</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">242</context>
+ <context context-type="linenumber">243</context>
</context-group>
</trans-unit>
<trans-unit id="7890599006071681081" datatype="html">
<source>Remove all</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">243</context>
+ <context context-type="linenumber">244</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">249</context>
+ <context context-type="linenumber">250</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">255</context>
+ <context context-type="linenumber">256</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">261</context>
+ <context context-type="linenumber">262</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">267</context>
+ <context context-type="linenumber">268</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">274</context>
+ <context context-type="linenumber">275</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">280</context>
+ <context context-type="linenumber">281</context>
</context-group>
</trans-unit>
<trans-unit id="8636414563726517994" datatype="html">
<source>Remove correspondents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">248</context>
+ <context context-type="linenumber">249</context>
</context-group>
</trans-unit>
<trans-unit id="5305293055593064952" datatype="html">
<source>Remove document types</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">254</context>
+ <context context-type="linenumber">255</context>
</context-group>
</trans-unit>
<trans-unit id="2400388879708187" datatype="html">
<source>Remove storage paths</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">260</context>
+ <context context-type="linenumber">261</context>
</context-group>
</trans-unit>
<trans-unit id="4324304327041955720" datatype="html">
<source>Remove custom fields</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">266</context>
+ <context context-type="linenumber">267</context>
</context-group>
</trans-unit>
<trans-unit id="8367536502602515064" datatype="html">
<source>Remove owners</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">273</context>
+ <context context-type="linenumber">274</context>
</context-group>
</trans-unit>
<trans-unit id="3393772184866313281" datatype="html">
<source>Remove permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">279</context>
+ <context context-type="linenumber">280</context>
</context-group>
</trans-unit>
<trans-unit id="3145629643370481114" datatype="html">
<source>View permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">282</context>
+ <context context-type="linenumber">283</context>
</context-group>
</trans-unit>
<trans-unit id="1946660694635960249" datatype="html">
<source>Edit permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">301</context>
+ <context context-type="linenumber">302</context>
</context-group>
</trans-unit>
<trans-unit id="8987736563240025468" datatype="html">
<source>Email subject</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">329</context>
+ <context context-type="linenumber">330</context>
</context-group>
</trans-unit>
<trans-unit id="8239445959209739142" datatype="html">
<source>Email body</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">330</context>
+ <context context-type="linenumber">331</context>
</context-group>
</trans-unit>
<trans-unit id="1222152280703048012" datatype="html">
<source>Email recipients</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">331</context>
+ <context context-type="linenumber">332</context>
</context-group>
</trans-unit>
<trans-unit id="7916910101279824329" datatype="html">
<source>Attach document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">332</context>
+ <context context-type="linenumber">333</context>
</context-group>
</trans-unit>
<trans-unit id="5028001922785731600" datatype="html">
<source>Webhook url</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">340</context>
+ <context context-type="linenumber">341</context>
</context-group>
</trans-unit>
<trans-unit id="7491983459027245019" datatype="html">
<source>Use parameters for webhook body</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">342</context>
+ <context context-type="linenumber">343</context>
</context-group>
</trans-unit>
<trans-unit id="4078214298308732810" datatype="html">
<source>Send webhook payload as JSON</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">343</context>
+ <context context-type="linenumber">344</context>
</context-group>
</trans-unit>
<trans-unit id="6806149889743731985" datatype="html">
<source>Webhook params</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">346</context>
+ <context context-type="linenumber">347</context>
</context-group>
</trans-unit>
<trans-unit id="7089924379374330" datatype="html">
<source>Webhook body</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">348</context>
+ <context context-type="linenumber">349</context>
</context-group>
</trans-unit>
<trans-unit id="3829826512656746316" datatype="html">
<source>Webhook headers</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">350</context>
+ <context context-type="linenumber">351</context>
</context-group>
</trans-unit>
<trans-unit id="2114525789021600887" datatype="html">
<source>Include document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
- <context context-type="linenumber">351</context>
+ <context context-type="linenumber">352</context>
</context-group>
</trans-unit>
<trans-unit id="4626030417479279989" datatype="html">
<source>Consume Folder</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">64</context>
+ <context context-type="linenumber">65</context>
</context-group>
</trans-unit>
<trans-unit id="526966086395145275" datatype="html">
<source>API Upload</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">68</context>
+ <context context-type="linenumber">69</context>
</context-group>
</trans-unit>
<trans-unit id="7502272564743467653" datatype="html">
<source>Mail Fetch</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">72</context>
+ <context context-type="linenumber">73</context>
</context-group>
</trans-unit>
<trans-unit id="235571817610183244" datatype="html">
<source>Web UI</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">76</context>
+ <context context-type="linenumber">77</context>
</context-group>
</trans-unit>
<trans-unit id="3553216189604488439" datatype="html">
<source>Modified</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">91</context>
+ <context context-type="linenumber">92</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
<source>Custom Field</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">95</context>
+ <context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit id="8696908693776094667" datatype="html">
<source>Consumption Started</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">102</context>
+ <context context-type="linenumber">103</context>
</context-group>
</trans-unit>
<trans-unit id="7858311467093621703" datatype="html">
<source>Document Added</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">106</context>
+ <context context-type="linenumber">107</context>
</context-group>
</trans-unit>
<trans-unit id="7955486237346046731" datatype="html">
<source>Document Updated</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">110</context>
+ <context context-type="linenumber">111</context>
</context-group>
</trans-unit>
<trans-unit id="9172233176401579786" datatype="html">
<source>Scheduled</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">114</context>
+ <context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit id="5502398334173581061" datatype="html">
<source>Assignment</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">121</context>
+ <context context-type="linenumber">122</context>
</context-group>
</trans-unit>
<trans-unit id="6234812824772766804" datatype="html">
<source>Removal</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">125</context>
+ <context context-type="linenumber">126</context>
</context-group>
</trans-unit>
<trans-unit id="4206419737792796794" datatype="html">
<source>Webhook</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">133</context>
+ <context context-type="linenumber">134</context>
</context-group>
</trans-unit>
<trans-unit id="3138206142174978019" datatype="html">
<source>Create new workflow</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">229</context>
+ <context context-type="linenumber">231</context>
</context-group>
</trans-unit>
<trans-unit id="5996779210524133604" datatype="html">
<source>Edit workflow</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
- <context context-type="linenumber">233</context>
+ <context context-type="linenumber">235</context>
</context-group>
</trans-unit>
<trans-unit id="7376342558017986274" datatype="html">
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
+ <pngx-input-custom-fields-values formControlName="assign_custom_fields_values" [selectedFields]="formGroup.get('assign_custom_fields').value" (removeSelectedField)="removeSelectedCustomField($event, formGroup)"></pngx-input-custom-fields-values>
</div>
<div class="col">
<pngx-input-select i18n-title title="Assign owner" [items]="users" bindLabel="username" formControlName="assign_owner" [allowNull]="true"></pngx-input-select>
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { FormsModule, ReactiveFormsModule } from '@angular/forms'
+import {
+ FormControl,
+ FormGroup,
+ FormsModule,
+ ReactiveFormsModule,
+} from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of } from 'rxjs'
expect(component.objectForm.get('actions').value[0].email).toBeNull()
expect(component.objectForm.get('actions').value[0].webhook).toBeNull()
})
+
+ it('should remove selected custom field from the form group', () => {
+ const formGroup = new FormGroup({
+ assign_custom_fields: new FormControl([1, 2, 3]),
+ })
+
+ component.removeSelectedCustomField(2, formGroup)
+ expect(formGroup.get('assign_custom_fields').value).toEqual([1, 3])
+
+ component.removeSelectedCustomField(1, formGroup)
+ expect(formGroup.get('assign_custom_fields').value).toEqual([3])
+
+ component.removeSelectedCustomField(3, formGroup)
+ expect(formGroup.get('assign_custom_fields').value).toEqual([])
+ })
})
import { SettingsService } from 'src/app/services/settings.service'
import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component'
import { CheckComponent } from '../../input/check/check.component'
+import { CustomFieldsValuesComponent } from '../../input/custom-fields-values/custom-fields-values.component'
import { EntriesComponent } from '../../input/entries/entries.component'
import { NumberComponent } from '../../input/number/number.component'
import { PermissionsGroupComponent } from '../../input/permissions/permissions-group/permissions-group.component'
SelectComponent,
TextAreaComponent,
TagsComponent,
+ CustomFieldsValuesComponent,
PermissionsGroupComponent,
PermissionsUserComponent,
ConfirmButtonComponent,
assign_change_users: new FormControl(action.assign_change_users),
assign_change_groups: new FormControl(action.assign_change_groups),
assign_custom_fields: new FormControl(action.assign_custom_fields),
+ assign_custom_fields_values: new FormControl(
+ action.assign_custom_fields_values
+ ),
remove_tags: new FormControl(action.remove_tags),
remove_all_tags: new FormControl(action.remove_all_tags),
remove_document_types: new FormControl(action.remove_document_types),
assign_change_users: [],
assign_change_groups: [],
assign_custom_fields: [],
+ assign_custom_fields_values: {},
remove_tags: [],
remove_all_tags: false,
remove_document_types: [],
})
super.save()
}
+
+ public removeSelectedCustomField(fieldId: number, group: FormGroup) {
+ group
+ .get('assign_custom_fields')
+ .setValue(
+ group.get('assign_custom_fields').value.filter((id) => id !== fieldId)
+ )
+ }
}
--- /dev/null
+<div class="list-group mt-3 selected-fields">
+ @for (fieldId of selectedFields; track fieldId) {
+ <div class="list-group-item
+ d-flex
+ justify-content-between
+ align-items-center">
+ @switch (getCustomField(fieldId)?.data_type) {
+ @case (CustomFieldDataType.String) {
+ <pngx-input-text [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-text>
+ }
+ @case (CustomFieldDataType.Date) {
+ <pngx-input-date [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-date>
+ }
+ @case (CustomFieldDataType.Integer) {
+ <pngx-input-number [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"
+ [showAdd]="false"></pngx-input-number>
+ }
+ @case (CustomFieldDataType.Float) {
+ <pngx-input-number [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"
+ [showAdd]="false"
+ [step]=".1"></pngx-input-number>
+ }
+ @case (CustomFieldDataType.Monetary) {
+ <pngx-input-monetary [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [defaultCurrency]="getCustomField(fieldId)?.extra_data?.default_currency"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-monetary>
+ }
+ @case (CustomFieldDataType.Boolean) {
+ <pngx-input-check [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-check>
+ }
+ @case (CustomFieldDataType.Url) {
+ <pngx-input-url [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-url>
+ }
+ @case (CustomFieldDataType.DocumentLink) {
+ <pngx-input-document-link [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [horizontal]="true"></pngx-input-document-link>
+ }
+ @case (CustomFieldDataType.Select) {
+ <pngx-input-select [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
+ [title]="getCustomField(fieldId)?.name"
+ class="flex-grow-1"
+ [items]="getCustomField(fieldId)?.extra_data.select_options"
+ class="flex-grow-1"
+ bindLabel="label"
+ [allowNull]="true"
+ [horizontal]="true"></pngx-input-select>
+ }
+ }
+ <button type="button" class="btn btn-link text-danger" (click)="removeSelectedField.next(fieldId)">
+ <i-bs name="trash"></i-bs>
+ </button>
+ </div>
+ }
+</div>
--- /dev/null
+:host ::ng-deep .list-group-item .mb-3 {
+ margin-bottom: 0 !important;
+}
--- /dev/null
+import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
+import { provideHttpClientTesting } from '@angular/common/http/testing'
+import { ComponentFixture, TestBed } from '@angular/core/testing'
+import {
+ FormsModule,
+ NG_VALUE_ACCESSOR,
+ ReactiveFormsModule,
+} from '@angular/forms'
+import { of } from 'rxjs'
+import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
+import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
+import { CustomFieldsValuesComponent } from './custom-fields-values.component'
+
+describe('CustomFieldsValuesComponent', () => {
+ let component: CustomFieldsValuesComponent
+ let fixture: ComponentFixture<CustomFieldsValuesComponent>
+ let customFieldsService: CustomFieldsService
+
+ beforeEach(async () => {
+ TestBed.configureTestingModule({
+ imports: [FormsModule, ReactiveFormsModule, CustomFieldsValuesComponent],
+ providers: [
+ provideHttpClient(withInterceptorsFromDi()),
+ provideHttpClientTesting(),
+ ],
+ }).compileComponents()
+
+ fixture = TestBed.createComponent(CustomFieldsValuesComponent)
+ fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
+ component = fixture.componentInstance
+ customFieldsService = TestBed.inject(CustomFieldsService)
+ jest.spyOn(customFieldsService, 'listAll').mockReturnValue(
+ of({
+ all: [1],
+ count: 1,
+ results: [
+ {
+ id: 1,
+ name: 'Field 1',
+ data_type: CustomFieldDataType.String,
+ } as CustomField,
+ ],
+ })
+ )
+ fixture.detectChanges()
+ })
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CustomFieldsValuesComponent)
+ component = fixture.componentInstance
+ fixture.detectChanges()
+ })
+
+ it('should set selectedFields and map values correctly', () => {
+ component.value = { 1: 'value1' }
+ component.selectedFields = [1, 2]
+ expect(component.selectedFields).toEqual([1, 2])
+ expect(component.value).toEqual({ 1: 'value1', 2: null })
+ })
+
+ it('should return the correct custom field by id', () => {
+ const field = component.getCustomField(1)
+ expect(field).toEqual({
+ id: 1,
+ name: 'Field 1',
+ data_type: CustomFieldDataType.String,
+ } as CustomField)
+ })
+})
--- /dev/null
+import {
+ Component,
+ EventEmitter,
+ forwardRef,
+ Input,
+ Output,
+} from '@angular/core'
+import {
+ FormsModule,
+ NG_VALUE_ACCESSOR,
+ ReactiveFormsModule,
+} from '@angular/forms'
+import { RouterModule } from '@angular/router'
+import { NgSelectModule } from '@ng-select/ng-select'
+import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
+import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
+import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
+import { AbstractInputComponent } from '../abstract-input'
+import { CheckComponent } from '../check/check.component'
+import { DateComponent } from '../date/date.component'
+import { DocumentLinkComponent } from '../document-link/document-link.component'
+import { MonetaryComponent } from '../monetary/monetary.component'
+import { NumberComponent } from '../number/number.component'
+import { SelectComponent } from '../select/select.component'
+import { TextComponent } from '../text/text.component'
+import { UrlComponent } from '../url/url.component'
+
+@Component({
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CustomFieldsValuesComponent),
+ multi: true,
+ },
+ ],
+ selector: 'pngx-input-custom-fields-values',
+ templateUrl: './custom-fields-values.component.html',
+ styleUrl: './custom-fields-values.component.scss',
+ imports: [
+ TextComponent,
+ DateComponent,
+ NumberComponent,
+ DocumentLinkComponent,
+ UrlComponent,
+ SelectComponent,
+ MonetaryComponent,
+ CheckComponent,
+ NgSelectModule,
+ FormsModule,
+ ReactiveFormsModule,
+ RouterModule,
+ NgxBootstrapIconsModule,
+ ],
+})
+export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
+ public CustomFieldDataType = CustomFieldDataType
+
+ constructor(customFieldsService: CustomFieldsService) {
+ super()
+ customFieldsService.listAll().subscribe((items) => {
+ this.fields = items.results
+ })
+ }
+
+ private fields: CustomField[]
+
+ private _selectedFields: number[]
+
+ @Input()
+ set selectedFields(newFields: number[]) {
+ this._selectedFields = newFields
+ // map the selected fields to an object with field_id as key and value as value
+ this.value = newFields.reduce((acc, fieldId) => {
+ acc[fieldId] = this.value?.[fieldId] || null
+ return acc
+ }, {})
+ this.onChange(this.value)
+ }
+
+ get selectedFields(): number[] {
+ return this._selectedFields
+ }
+
+ @Output()
+ public removeSelectedField: EventEmitter<number> = new EventEmitter<number>()
+
+ public getCustomField(id: number): CustomField {
+ return this.fields.find((field) => field.id === id)
+ }
+}
assign_custom_fields?: number[] // [CustomField.id]
+ assign_custom_fields_values?: object
+
remove_tags?: number[] // Tag.id
remove_all_tags?: boolean
}
set_permissions_for_object(permissions=permissions, object=document)
- if self.metadata.custom_field_ids:
- for field_id in self.metadata.custom_field_ids:
- field = CustomField.objects.get(pk=field_id)
- CustomFieldInstance.objects.create(
- field=field,
- document=document,
- ) # adds to document
+ if self.metadata.custom_fields:
+ for field in CustomField.objects.filter(
+ id__in=self.metadata.custom_fields.keys(),
+ ).distinct():
+ value_field_name = CustomFieldInstance.get_value_field_name(
+ data_type=field.data_type,
+ )
+ args = {
+ "field": field,
+ "document": document,
+ value_field_name: self.metadata.custom_fields.get(field.id, None),
+ }
+ CustomFieldInstance.objects.create(**args) # adds to document
def _write(self, storage_type, source, target):
with (
view_groups: list[int] | None = None
change_users: list[int] | None = None
change_groups: list[int] | None = None
- custom_field_ids: list[int] | None = None
+ custom_fields: dict | None = None
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
"""
self.change_groups.extend(other.change_groups)
self.change_groups = list(set(self.change_groups))
- if self.custom_field_ids is None:
- self.custom_field_ids = other.custom_field_ids
- elif other.custom_field_ids is not None:
- self.custom_field_ids.extend(other.custom_field_ids)
- self.custom_field_ids = list(set(self.custom_field_ids))
+ if self.custom_fields is None:
+ self.custom_fields = other.custom_fields
+ elif other.custom_fields is not None:
+ self.custom_fields.update(other.custom_fields)
return self
only_with_perms_in=["change_document"],
).values_list("id", flat=True),
)
- overrides.custom_field_ids = list(
- doc.custom_fields.values_list("field", flat=True),
- )
+ overrides.custom_fields = {
+ custom_field.id: custom_field.value
+ for custom_field in doc.custom_fields.all()
+ }
groups_with_perms = get_groups_with_perms(
doc,
--- /dev/null
+# Generated by Django 5.1.6 on 2025-03-01 18:10
+
+from django.db import migrations
+from django.db import models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("documents", "1064_delete_log"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="workflowaction",
+ name="assign_custom_fields_values",
+ field=models.JSONField(
+ blank=True,
+ help_text="Optional values to assign to the custom fields.",
+ null=True,
+ verbose_name="custom field values",
+ default=dict,
+ ),
+ ),
+ ]
verbose_name=_("assign these custom fields"),
)
+ assign_custom_fields_values = models.JSONField(
+ _("custom field values"),
+ null=True,
+ blank=True,
+ help_text=_(
+ "Optional values to assign to the custom fields.",
+ ),
+ default=dict,
+ )
+
remove_tags = models.ManyToManyField(
Tag,
blank=True,
"assign_change_users",
"assign_change_groups",
"assign_custom_fields",
+ "assign_custom_fields_values",
"remove_all_tags",
"remove_tags",
"remove_all_correspondents",
if action.assign_custom_fields.exists():
if not use_overrides:
for field in action.assign_custom_fields.all():
- if not CustomFieldInstance.objects.filter(
+ value_field_name = CustomFieldInstance.get_value_field_name(
+ data_type=field.data_type,
+ )
+ args = {
+ value_field_name: action.assign_custom_fields_values.get(
+ str(field.pk),
+ None,
+ ),
+ }
+ # for some reason update_or_create doesn't work here
+ instance = CustomFieldInstance.objects.filter(
field=field,
document=document,
- ).exists():
- # can be triggered on existing docs, so only add the field if it doesn't already exist
+ ).first()
+ if instance:
+ setattr(instance, value_field_name, args[value_field_name])
+ instance.save()
+ else:
CustomFieldInstance.objects.create(
+ **args,
field=field,
document=document,
)
else:
- overrides.custom_field_ids = list(
- set(
- (overrides.custom_field_ids or [])
- + list(
- action.assign_custom_fields.values_list("pk", flat=True),
- ),
- ),
+ if overrides.custom_fields is None:
+ overrides.custom_fields = {}
+ overrides.custom_fields.update(
+ {
+ field.pk: action.assign_custom_fields_values.get(
+ str(field.pk),
+ None,
+ )
+ for field in action.assign_custom_fields.all()
+ },
)
def removal_action():
if not use_overrides:
CustomFieldInstance.objects.filter(document=document).delete()
else:
- overrides.custom_field_ids = None
+ overrides.custom_fields = None
elif action.remove_custom_fields.exists():
if not use_overrides:
CustomFieldInstance.objects.filter(
field__in=action.remove_custom_fields.all(),
document=document,
).delete()
- elif overrides.custom_field_ids:
+ elif overrides.custom_fields:
for field in action.remove_custom_fields.filter(
- pk__in=overrides.custom_field_ids,
+ pk__in=overrides.custom_fields.keys(),
):
- overrides.custom_field_ids.remove(field.pk)
+ overrides.custom_fields.pop(field.pk, None)
def email_action():
if not settings.EMAIL_ENABLED:
from documents.caching import CLASSIFIER_HASH_KEY
from documents.caching import CLASSIFIER_MODIFIED_KEY
from documents.caching import CLASSIFIER_VERSION_KEY
+from documents.data_models import DocumentSource
from documents.models import Correspondent
from documents.models import CustomField
from documents.models import CustomFieldInstance
from documents.models import ShareLink
from documents.models import StoragePath
from documents.models import Tag
+from documents.models import Workflow
+from documents.models import WorkflowAction
from documents.models import WorkflowTrigger
+from documents.signals.handlers import run_workflows
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import DocumentConsumeDelayMixin
self.assertEqual(input_doc.original_file.name, "simple.pdf")
self.assertEqual(overrides.filename, "simple.pdf")
- self.assertEqual(overrides.custom_field_ids, [custom_field.id])
+ self.assertEqual(overrides.custom_fields, {custom_field.id: None})
+
+ def test_upload_with_custom_fields_and_workflow(self):
+ """
+ GIVEN: A document with a source file
+ WHEN: Upload the document with custom fields and a workflow
+ THEN: Metadata is set correctly, mimicking what happens in the real consumer plugin
+ """
+ self.consume_file_mock.return_value = celery.result.AsyncResult(
+ id=str(uuid.uuid4()),
+ )
+
+ cf = CustomField.objects.create(
+ name="stringfield",
+ data_type=CustomField.FieldDataType.STRING,
+ )
+ cf2 = CustomField.objects.create(
+ name="intfield",
+ data_type=CustomField.FieldDataType.INT,
+ )
+
+ trigger1 = WorkflowTrigger.objects.create(
+ type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION,
+ sources=f"{DocumentSource.ApiUpload},{DocumentSource.ConsumeFolder},{DocumentSource.MailFetch}",
+ )
+ action1 = WorkflowAction.objects.create(
+ assign_title="Doc title",
+ )
+ action1.assign_custom_fields.add(cf2)
+ action1.assign_custom_fields_values = {cf2.id: 123}
+ action1.save()
+
+ w1 = Workflow.objects.create(
+ name="Workflow 1",
+ order=0,
+ )
+ w1.triggers.add(trigger1)
+ w1.actions.add(action1)
+ w1.save()
+
+ with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
+ response = self.client.post(
+ "/api/documents/post_document/",
+ {
+ "document": f,
+ "custom_fields": [cf.id],
+ },
+ )
+
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ self.consume_file_mock.assert_called_once()
+
+ input_doc, overrides = self.get_last_consume_delay_call_args()
+
+ new_overrides, msg = run_workflows(
+ trigger_type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION,
+ document=input_doc,
+ logging_group=None,
+ overrides=overrides,
+ )
+ overrides.update(new_overrides)
+ self.assertEqual(overrides.custom_fields, {cf.id: None, cf2.id: 123})
def test_upload_with_webui_source(self):
"""
with self.get_consumer(
self.get_test_file(),
- DocumentMetadataOverrides(custom_field_ids=[cf1.id, cf3.id]),
+ DocumentMetadataOverrides(
+ custom_fields={cf1.id: "value1", cf3.id: "http://example.com"},
+ ),
) as consumer:
consumer.run()
self.assertIn(cf1, fields_used)
self.assertNotIn(cf2, fields_used)
self.assertIn(cf3, fields_used)
+ self.assertEqual(document.custom_fields.get(field=cf1).value, "value1")
+ self.assertEqual(
+ document.custom_fields.get(field=cf3).value,
+ "http://example.com",
+ )
self._assert_first_last_send_progress()
def testOverrideAsn(self):
action.assign_change_groups.add(self.group1.pk)
action.assign_custom_fields.add(self.cf1.pk)
action.assign_custom_fields.add(self.cf2.pk)
+ action.assign_custom_fields_values = {
+ self.cf2.pk: 42,
+ }
action.save()
w = Workflow.objects.create(
name="Workflow 1",
list(document.custom_fields.all().values_list("field", flat=True)),
[self.cf1.pk, self.cf2.pk],
)
+ self.assertEqual(
+ document.custom_fields.get(field=self.cf2.pk).value,
+ 42,
+ )
info = cm.output[0]
expected_str = f"Document matched {trigger} from {w}"
def test_document_updated_workflow_existing_custom_field(self):
"""
GIVEN:
- - Existing workflow with UPDATED trigger and action that adds a custom field
+ - Existing workflow with UPDATED trigger and action that assigns a custom field with a value
WHEN:
- Document is updated that already contains the field
THEN:
- - Document update succeeds without trying to re-create the field
+ - Document update succeeds and updates the field
"""
trigger = WorkflowTrigger.objects.create(
type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED,
)
action = WorkflowAction.objects.create()
action.assign_custom_fields.add(self.cf1)
+ action.assign_custom_fields_values = {self.cf1.pk: "new value"}
+ action.save()
w = Workflow.objects.create(
name="Workflow 1",
order=0,
format="json",
)
+ doc.refresh_from_db()
+ self.assertEqual(doc.custom_fields.get(field=self.cf1).value, "new value")
+
def test_document_updated_workflow_merge_permissions(self):
"""
GIVEN:
created=created,
asn=archive_serial_number,
owner_id=request.user.id,
- custom_field_ids=custom_field_ids,
+ # TODO: set values
+ custom_fields={cf_id: None for cf_id in custom_field_ids}
+ if custom_field_ids
+ else None,
)
async_task = consume_file.delay(
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-02-25 11:07-0800\n"
+"POT-Creation-Date: 2025-03-01 21:03-0800\n"
"PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n"
"Language-Team: English\n"
msgid "Documents"
msgstr ""
-#: documents/filters.py:370
+#: documents/filters.py:375
msgid "Value must be valid JSON."
msgstr ""
-#: documents/filters.py:389
+#: documents/filters.py:394
msgid "Invalid custom field query expression"
msgstr ""
-#: documents/filters.py:399
+#: documents/filters.py:404
msgid "Invalid expression list. Must be nonempty."
msgstr ""
-#: documents/filters.py:420
+#: documents/filters.py:425
msgid "Invalid logical operator {op!r}"
msgstr ""
-#: documents/filters.py:434
+#: documents/filters.py:439
msgid "Maximum number of query conditions exceeded."
msgstr ""
-#: documents/filters.py:499
+#: documents/filters.py:504
msgid "{name!r} is not a valid custom field."
msgstr ""
-#: documents/filters.py:536
+#: documents/filters.py:541
msgid "{data_type} does not support query expr {expr!r}."
msgstr ""
-#: documents/filters.py:644
+#: documents/filters.py:649
msgid "Maximum nesting depth exceeded."
msgstr ""
-#: documents/filters.py:829
+#: documents/filters.py:834
msgid "Custom field not found"
msgstr ""
msgid "Automatic"
msgstr ""
-#: documents/models.py:67 documents/models.py:433 documents/models.py:1526
+#: documents/models.py:67 documents/models.py:433 documents/models.py:1536
#: paperless_mail/models.py:23 paperless_mail/models.py:143
msgid "name"
msgstr ""
msgstr ""
#: documents/models.py:295 documents/models.py:761 documents/models.py:815
-#: documents/models.py:1569
+#: documents/models.py:1579
msgid "document"
msgstr ""
msgid "assign these custom fields"
msgstr ""
-#: documents/models.py:1398
+#: documents/models.py:1395
+msgid "custom field values"
+msgstr ""
+
+#: documents/models.py:1399
+msgid "Optional values to assign to the custom fields."
+msgstr ""
+
+#: documents/models.py:1408
msgid "remove these tag(s)"
msgstr ""
-#: documents/models.py:1403
+#: documents/models.py:1413
msgid "remove all tags"
msgstr ""
-#: documents/models.py:1410
+#: documents/models.py:1420
msgid "remove these document type(s)"
msgstr ""
-#: documents/models.py:1415
+#: documents/models.py:1425
msgid "remove all document types"
msgstr ""
-#: documents/models.py:1422
+#: documents/models.py:1432
msgid "remove these correspondent(s)"
msgstr ""
-#: documents/models.py:1427
+#: documents/models.py:1437
msgid "remove all correspondents"
msgstr ""
-#: documents/models.py:1434
+#: documents/models.py:1444
msgid "remove these storage path(s)"
msgstr ""
-#: documents/models.py:1439
+#: documents/models.py:1449
msgid "remove all storage paths"
msgstr ""
-#: documents/models.py:1446
+#: documents/models.py:1456
msgid "remove these owner(s)"
msgstr ""
-#: documents/models.py:1451
+#: documents/models.py:1461
msgid "remove all owners"
msgstr ""
-#: documents/models.py:1458
+#: documents/models.py:1468
msgid "remove view permissions for these users"
msgstr ""
-#: documents/models.py:1465
+#: documents/models.py:1475
msgid "remove view permissions for these groups"
msgstr ""
-#: documents/models.py:1472
+#: documents/models.py:1482
msgid "remove change permissions for these users"
msgstr ""
-#: documents/models.py:1479
+#: documents/models.py:1489
msgid "remove change permissions for these groups"
msgstr ""
-#: documents/models.py:1484
+#: documents/models.py:1494
msgid "remove all permissions"
msgstr ""
-#: documents/models.py:1491
+#: documents/models.py:1501
msgid "remove these custom fields"
msgstr ""
-#: documents/models.py:1496
+#: documents/models.py:1506
msgid "remove all custom fields"
msgstr ""
-#: documents/models.py:1505
+#: documents/models.py:1515
msgid "email"
msgstr ""
-#: documents/models.py:1514
+#: documents/models.py:1524
msgid "webhook"
msgstr ""
-#: documents/models.py:1518
+#: documents/models.py:1528
msgid "workflow action"
msgstr ""
-#: documents/models.py:1519
+#: documents/models.py:1529
msgid "workflow actions"
msgstr ""
-#: documents/models.py:1528 paperless_mail/models.py:145
+#: documents/models.py:1538 paperless_mail/models.py:145
msgid "order"
msgstr ""
-#: documents/models.py:1534
+#: documents/models.py:1544
msgid "triggers"
msgstr ""
-#: documents/models.py:1541
+#: documents/models.py:1551
msgid "actions"
msgstr ""
-#: documents/models.py:1544 paperless_mail/models.py:154
+#: documents/models.py:1554 paperless_mail/models.py:154
msgid "enabled"
msgstr ""
-#: documents/models.py:1555
+#: documents/models.py:1565
msgid "workflow"
msgstr ""
-#: documents/models.py:1559
+#: documents/models.py:1569
msgid "workflow trigger type"
msgstr ""
-#: documents/models.py:1573
+#: documents/models.py:1583
msgid "date run"
msgstr ""
-#: documents/models.py:1579
+#: documents/models.py:1589
msgid "workflow run"
msgstr ""
-#: documents/models.py:1580
+#: documents/models.py:1590
msgid "workflow runs"
msgstr ""
-#: documents/serialisers.py:128
+#: documents/serialisers.py:134
#, python-format
msgid "Invalid regular expression: %(error)s"
msgstr ""
-#: documents/serialisers.py:554
+#: documents/serialisers.py:560
msgid "Invalid color."
msgstr ""
-#: documents/serialisers.py:1570
+#: documents/serialisers.py:1576
#, python-format
msgid "File type %(type)s not supported"
msgstr ""
-#: documents/serialisers.py:1659
+#: documents/serialisers.py:1665
msgid "Invalid variable detected."
msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
-#: paperless/apps.py:10
+#: paperless/apps.py:11
msgid "Paperless"
msgstr ""
msgid "paperless application settings"
msgstr ""
-#: paperless/settings.py:721
+#: paperless/settings.py:724
msgid "English (US)"
msgstr ""
-#: paperless/settings.py:722
+#: paperless/settings.py:725
msgid "Arabic"
msgstr ""
-#: paperless/settings.py:723
+#: paperless/settings.py:726
msgid "Afrikaans"
msgstr ""
-#: paperless/settings.py:724
+#: paperless/settings.py:727
msgid "Belarusian"
msgstr ""
-#: paperless/settings.py:725
+#: paperless/settings.py:728
msgid "Bulgarian"
msgstr ""
-#: paperless/settings.py:726
+#: paperless/settings.py:729
msgid "Catalan"
msgstr ""
-#: paperless/settings.py:727
+#: paperless/settings.py:730
msgid "Czech"
msgstr ""
-#: paperless/settings.py:728
+#: paperless/settings.py:731
msgid "Danish"
msgstr ""
-#: paperless/settings.py:729
+#: paperless/settings.py:732
msgid "German"
msgstr ""
-#: paperless/settings.py:730
+#: paperless/settings.py:733
msgid "Greek"
msgstr ""
-#: paperless/settings.py:731
+#: paperless/settings.py:734
msgid "English (GB)"
msgstr ""
-#: paperless/settings.py:732
+#: paperless/settings.py:735
msgid "Spanish"
msgstr ""
-#: paperless/settings.py:733
+#: paperless/settings.py:736
msgid "Finnish"
msgstr ""
-#: paperless/settings.py:734
+#: paperless/settings.py:737
msgid "French"
msgstr ""
-#: paperless/settings.py:735
+#: paperless/settings.py:738
msgid "Hungarian"
msgstr ""
-#: paperless/settings.py:736
+#: paperless/settings.py:739
msgid "Italian"
msgstr ""
-#: paperless/settings.py:737
+#: paperless/settings.py:740
msgid "Japanese"
msgstr ""
-#: paperless/settings.py:738
+#: paperless/settings.py:741
msgid "Korean"
msgstr ""
-#: paperless/settings.py:739
+#: paperless/settings.py:742
msgid "Luxembourgish"
msgstr ""
-#: paperless/settings.py:740
+#: paperless/settings.py:743
msgid "Norwegian"
msgstr ""
-#: paperless/settings.py:741
+#: paperless/settings.py:744
msgid "Dutch"
msgstr ""
-#: paperless/settings.py:742
+#: paperless/settings.py:745
msgid "Polish"
msgstr ""
-#: paperless/settings.py:743
+#: paperless/settings.py:746
msgid "Portuguese (Brazil)"
msgstr ""
-#: paperless/settings.py:744
+#: paperless/settings.py:747
msgid "Portuguese"
msgstr ""
-#: paperless/settings.py:745
+#: paperless/settings.py:748
msgid "Romanian"
msgstr ""
-#: paperless/settings.py:746
+#: paperless/settings.py:749
msgid "Russian"
msgstr ""
-#: paperless/settings.py:747
+#: paperless/settings.py:750
msgid "Slovak"
msgstr ""
-#: paperless/settings.py:748
+#: paperless/settings.py:751
msgid "Slovenian"
msgstr ""
-#: paperless/settings.py:749
+#: paperless/settings.py:752
msgid "Serbian"
msgstr ""
-#: paperless/settings.py:750
+#: paperless/settings.py:753
msgid "Swedish"
msgstr ""
-#: paperless/settings.py:751
+#: paperless/settings.py:754
msgid "Turkish"
msgstr ""
-#: paperless/settings.py:752
+#: paperless/settings.py:755
msgid "Ukrainian"
msgstr ""
-#: paperless/settings.py:753
+#: paperless/settings.py:756
msgid "Chinese Simplified"
msgstr ""
-#: paperless/settings.py:754
+#: paperless/settings.py:757
msgid "Chinese Traditional"
msgstr ""