]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Enhancement: support assigning custom field values in workflows (#9272)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Wed, 5 Mar 2025 20:30:19 +0000 (12:30 -0800)
committerGitHub <noreply@github.com>
Wed, 5 Mar 2025 20:30:19 +0000 (12:30 -0800)
20 files changed:
src-ui/messages.xlf
src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts
src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts
src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.html [new file with mode: 0644]
src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.scss [new file with mode: 0644]
src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.spec.ts [new file with mode: 0644]
src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.ts [new file with mode: 0644]
src-ui/src/app/data/workflow-action.ts
src/documents/consumer.py
src/documents/data_models.py
src/documents/migrations/1065_workflowaction_assign_custom_fields_values.py [new file with mode: 0644]
src/documents/models.py
src/documents/serialisers.py
src/documents/signals/handlers.py
src/documents/tests/test_api_documents.py
src/documents/tests/test_consumer.py
src/documents/tests/test_workflows.py
src/documents/views.py
src/locale/en_US/LC_MESSAGES/django.po

index 58d8d0d4cb56f74af40e9d7987ff4f250a996c9e..cd1a979503c6b1d573e96b56ab23629b3f26fab6 100644 (file)
         </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">
index add7878f483a12f5175b973fc7b2d92e01f52b57..4e5d5ba9b4105e801d0ae1e9adc9ccd929d3e7f8 100644 (file)
             <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>
index 24af7916df1985867aedc96c8facd53d738a8b17..930164dce132b6b8e96a17cbdd32a6c765a94f55 100644 (file)
@@ -2,7 +2,12 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop'
 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'
@@ -369,4 +374,19 @@ describe('WorkflowEditDialogComponent', () => {
     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([])
+  })
 })
index 5facc5ccef07d12cce39305efb04a01d13ed9b2b..a4a06ba04eefe626ad7cb67384be7fc28704445f 100644 (file)
@@ -47,6 +47,7 @@ import { WorkflowService } from 'src/app/services/rest/workflow.service'
 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'
@@ -151,6 +152,7 @@ const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
     SelectComponent,
     TextAreaComponent,
     TagsComponent,
+    CustomFieldsValuesComponent,
     PermissionsGroupComponent,
     PermissionsUserComponent,
     ConfirmButtonComponent,
@@ -439,6 +441,9 @@ export class WorkflowEditDialogComponent
         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),
@@ -565,6 +570,7 @@ export class WorkflowEditDialogComponent
       assign_change_users: [],
       assign_change_groups: [],
       assign_custom_fields: [],
+      assign_custom_fields_values: {},
       remove_tags: [],
       remove_all_tags: false,
       remove_document_types: [],
@@ -643,4 +649,12 @@ export class WorkflowEditDialogComponent
       })
     super.save()
   }
+
+  public removeSelectedCustomField(fieldId: number, group: FormGroup) {
+    group
+      .get('assign_custom_fields')
+      .setValue(
+        group.get('assign_custom_fields').value.filter((id) => id !== fieldId)
+      )
+  }
 }
diff --git a/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.html b/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.html
new file mode 100644 (file)
index 0000000..f0886b1
--- /dev/null
@@ -0,0 +1,77 @@
+<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>
diff --git a/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.scss b/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.scss
new file mode 100644 (file)
index 0000000..a0c770f
--- /dev/null
@@ -0,0 +1,3 @@
+:host ::ng-deep .list-group-item .mb-3 {
+  margin-bottom: 0 !important;
+}
diff --git a/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.spec.ts b/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.spec.ts
new file mode 100644 (file)
index 0000000..82a0654
--- /dev/null
@@ -0,0 +1,69 @@
+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)
+  })
+})
diff --git a/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.ts b/src-ui/src/app/components/common/input/custom-fields-values/custom-fields-values.component.ts
new file mode 100644 (file)
index 0000000..477a339
--- /dev/null
@@ -0,0 +1,90 @@
+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)
+  }
+}
index 0d8316ecbbd74cc384950be2d57062fa0675f6b7..06c46806e850c4b48486beaa3fd2be054aa0bbac 100644 (file)
@@ -58,6 +58,8 @@ export interface WorkflowAction extends ObjectWithId {
 
   assign_custom_fields?: number[] // [CustomField.id]
 
+  assign_custom_fields_values?: object
+
   remove_tags?: number[] // Tag.id
 
   remove_all_tags?: boolean
index 4bf9ab89b9b957128d1c5e016749bad916033a24..04ba588d4244aa2a73e1b5eb56fc77896298f788 100644 (file)
@@ -806,13 +806,19 @@ class ConsumerPlugin(
             }
             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 (
index 406fe6b5aa491fcc3e4efc12c215d22bb428c3f9..fbba36dccdad039f416cfcadd032f2c43eb1e5cb 100644 (file)
@@ -29,7 +29,7 @@ class DocumentMetadataOverrides:
     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":
         """
@@ -81,11 +81,10 @@ class 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
 
@@ -114,9 +113,10 @@ class DocumentMetadataOverrides:
                 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,
diff --git a/src/documents/migrations/1065_workflowaction_assign_custom_fields_values.py b/src/documents/migrations/1065_workflowaction_assign_custom_fields_values.py
new file mode 100644 (file)
index 0000000..35fae02
--- /dev/null
@@ -0,0 +1,24 @@
+# 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,
+            ),
+        ),
+    ]
index e40ee8115e936316b83fb4be2b724d78fe46309a..7cff304ad7d1e5109901ae113388581d5a6bad0d 100644 (file)
@@ -1271,6 +1271,16 @@ class WorkflowAction(models.Model):
         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,
index c0487b7b85351fe869bdbe844326d18c0016d06f..38053baeec29114c909e54771aecd050c61282da 100644 (file)
@@ -2018,6 +2018,7 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
             "assign_change_users",
             "assign_change_groups",
             "assign_custom_fields",
+            "assign_custom_fields_values",
             "remove_all_tags",
             "remove_tags",
             "remove_all_correspondents",
index 7241924e4be60bfdcd90afcaa4f633be43b7608e..4eb7f72cce3fa55a7d2dc5dbb2aa1012559f9c3b 100644 (file)
@@ -770,23 +770,40 @@ def run_workflows(
         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():
@@ -944,18 +961,18 @@ def run_workflows(
             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:
index cd923b2818cbaadf9596a6842227322446427111..7258b33d35693c9dd201d3cb065c44131fb29434 100644 (file)
@@ -28,6 +28,7 @@ from documents.caching import CACHE_50_MINUTES
 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
@@ -39,7 +40,10 @@ from documents.models import SavedView
 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
 
@@ -1362,7 +1366,69 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
 
         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):
         """
index ff684804e94c679cf6c41723b47293a6285ffd74..96afa61d3f99e9ee2a73ff9bedd062995bf5409f 100644 (file)
@@ -408,7 +408,9 @@ class TestConsumer(
 
         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()
 
@@ -420,6 +422,11 @@ class TestConsumer(
         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):
index 94dcb76897a349ca9a3e1bdc4ae607febc39e881..3006594cc4d7ecaf325d1c23f85ad8aae320902c 100644 (file)
@@ -133,6 +133,9 @@ class TestWorkflows(
         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",
@@ -209,6 +212,10 @@ class TestWorkflows(
                     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}"
@@ -1215,11 +1222,11 @@ class TestWorkflows(
     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,
@@ -1227,6 +1234,8 @@ class TestWorkflows(
         )
         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,
@@ -1251,6 +1260,9 @@ class TestWorkflows(
             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:
index 46a7c0b6fcadf8e77933592554b4d773a1de3fbf..1d4cb52dd13edd872e4b0b615f477bf965632003 100644 (file)
@@ -1471,7 +1471,10 @@ class PostDocumentView(GenericAPIView):
             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(
index 5dad5273b2e9b2075cb95fbd718b2c2ace535eb3..88cfdc59dba21fb8976c385c8022c4e152634fae 100644 (file)
@@ -2,7 +2,7 @@ msgid ""
 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"
@@ -21,39 +21,39 @@ msgstr ""
 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 ""
 
@@ -89,7 +89,7 @@ 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 ""
@@ -256,7 +256,7 @@ msgid "The position of this document in your physical document archive."
 msgstr ""
 
 #: documents/models.py:295 documents/models.py:761 documents/models.py:815
-#: documents/models.py:1569
+#: documents/models.py:1579
 msgid "document"
 msgstr ""
 
@@ -1088,141 +1088,149 @@ 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 ""
 
@@ -1463,7 +1471,7 @@ msgstr ""
 msgid "Unable to parse URI {value}"
 msgstr ""
 
-#: paperless/apps.py:10
+#: paperless/apps.py:11
 msgid "Paperless"
 msgstr ""
 
@@ -1611,139 +1619,139 @@ 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 ""