fix:create-document
This commit is contained in:
parent
0d76578906
commit
640d8b1c28
@ -1,8 +0,0 @@
|
|||||||
@if (tag === undefined) {
|
|
||||||
@if (!clickable) {
|
|
||||||
<span class="badge private" i18n>Private</span>
|
|
||||||
}
|
|
||||||
@if (clickable) {
|
|
||||||
<a [title]="linkTitle" class="badge private" i18n>Private</a>
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
a {
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-word;
|
|
||||||
text-align: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.private {
|
|
||||||
background-color: #000000;
|
|
||||||
color: #ffffff;
|
|
||||||
opacity: .5;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
|
||||||
import { WarehouseComponent } from './warehouse.component'
|
|
||||||
import { Warehouse } from 'src/app/data/warehouse'
|
|
||||||
import { By } from '@angular/platform-browser'
|
|
||||||
|
|
||||||
const warehouse: Warehouse = {
|
|
||||||
id: 1,
|
|
||||||
type: 'Warehouse',
|
|
||||||
name: 'Warehouse1',
|
|
||||||
parent_warehouse: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('WarehouseComponent', () => {
|
|
||||||
let component: WarehouseComponent
|
|
||||||
let fixture: ComponentFixture<WarehouseComponent>
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [WarehouseComponent],
|
|
||||||
providers: [],
|
|
||||||
imports: [],
|
|
||||||
}).compileComponents()
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(WarehouseComponent)
|
|
||||||
component = fixture.componentInstance
|
|
||||||
fixture.detectChanges()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
it('should handle private warehouses', () => {
|
|
||||||
expect(
|
|
||||||
fixture.debugElement.query(By.css('span')).nativeElement.textContent
|
|
||||||
).toEqual('Private')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support clickable option', () => {
|
|
||||||
component.warehouse = warehouse
|
|
||||||
fixture.detectChanges()
|
|
||||||
expect(fixture.debugElement.query(By.css('a.badge'))).toBeNull()
|
|
||||||
component.clickable = true
|
|
||||||
fixture.detectChanges()
|
|
||||||
expect(fixture.debugElement.query(By.css('a.badge'))).not.toBeNull()
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,20 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
|
||||||
import { Warehouse } from 'src/app/data/warehouse'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'pngx-warehouse',
|
|
||||||
templateUrl: './warehouse.component.html',
|
|
||||||
styleUrls: ['./warehouse.component.scss'],
|
|
||||||
})
|
|
||||||
export class WarehouseComponent {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
warehouse: Warehouse
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
linkTitle: string = ''
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
clickable: boolean = false
|
|
||||||
}
|
|
@ -111,7 +111,7 @@
|
|||||||
(createNew)="createDocumentType($event)" [suggestions]="suggestions?.document_types" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }"></pngx-input-select>
|
(createNew)="createDocumentType($event)" [suggestions]="suggestions?.document_types" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }"></pngx-input-select>
|
||||||
<pngx-input-select [items]="storagePaths" i18n-title title="Storage path" formControlName="storage_path" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
<pngx-input-select [items]="storagePaths" i18n-title title="Storage path" formControlName="storage_path" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
||||||
(createNew)="createStoragePath($event)" [suggestions]="suggestions?.storage_paths" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }"></pngx-input-select>
|
(createNew)="createStoragePath($event)" [suggestions]="suggestions?.storage_paths" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }"></pngx-input-select>
|
||||||
<pngx-input-select [items]="warehouses" i18n-title title="Warehouse" formControlName="warehouses" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
<pngx-input-select [items]="warehouses" i18n-title title="Warehouse" formControlName="warehouse" [allowNull]="true" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)"
|
||||||
(createNew)="createWarehouse($event)" [suggestions]="suggestions?.warehouses" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Warehouse }"></pngx-input-select>
|
(createNew)="createWarehouse($event)" [suggestions]="suggestions?.warehouses" i18n-placeholder placeholder="Default" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Warehouse }"></pngx-input-select>
|
||||||
<pngx-input-tags formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
<pngx-input-tags formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
||||||
@for (fieldInstance of document?.custom_fields; track fieldInstance; let i = $index) {
|
@for (fieldInstance of document?.custom_fields; track fieldInstance; let i = $index) {
|
||||||
|
@ -148,7 +148,7 @@ export class DocumentDetailComponent
|
|||||||
correspondent: new FormControl(),
|
correspondent: new FormControl(),
|
||||||
document_type: new FormControl(),
|
document_type: new FormControl(),
|
||||||
storage_path: new FormControl(),
|
storage_path: new FormControl(),
|
||||||
warehouses: new FormControl(),
|
warehouse: new FormControl(),
|
||||||
archive_serial_number: new FormControl(),
|
archive_serial_number: new FormControl(),
|
||||||
tags: new FormControl([]),
|
tags: new FormControl([]),
|
||||||
permissions_form: new FormControl(null),
|
permissions_form: new FormControl(null),
|
||||||
@ -271,6 +271,17 @@ export class DocumentDetailComponent
|
|||||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe((result) => (this.correspondents = result.results))
|
.subscribe((result) => (this.correspondents = result.results))
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
this.permissionsService.currentUserCan(
|
||||||
|
PermissionAction.View,
|
||||||
|
PermissionType.Warehouse
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.warehouseService
|
||||||
|
.listAll()
|
||||||
|
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe((result) => (this.warehouses = result.results))
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.permissionsService.currentUserCan(
|
this.permissionsService.currentUserCan(
|
||||||
PermissionAction.View,
|
PermissionAction.View,
|
||||||
@ -293,17 +304,6 @@ export class DocumentDetailComponent
|
|||||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe((result) => (this.storagePaths = result.results))
|
.subscribe((result) => (this.storagePaths = result.results))
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
this.permissionsService.currentUserCan(
|
|
||||||
PermissionAction.View,
|
|
||||||
PermissionType.Warehouse
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.warehouseService
|
|
||||||
.listAll()
|
|
||||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
|
||||||
.subscribe((result) => (this.warehouses = result.results))
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
this.permissionsService.currentUserCan(
|
this.permissionsService.currentUserCan(
|
||||||
PermissionAction.View,
|
PermissionAction.View,
|
||||||
@ -427,7 +427,7 @@ export class DocumentDetailComponent
|
|||||||
correspondent: doc.correspondent,
|
correspondent: doc.correspondent,
|
||||||
document_type: doc.document_type,
|
document_type: doc.document_type,
|
||||||
storage_path: doc.storage_path,
|
storage_path: doc.storage_path,
|
||||||
warehouses: doc.warehouses,
|
warehouse: doc.warehouse,
|
||||||
archive_serial_number: doc.archive_serial_number,
|
archive_serial_number: doc.archive_serial_number,
|
||||||
tags: [...doc.tags],
|
tags: [...doc.tags],
|
||||||
permissions_form: {
|
permissions_form: {
|
||||||
@ -639,7 +639,7 @@ export class DocumentDetailComponent
|
|||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe(({ newWarehouse, warehouses }) => {
|
.subscribe(({ newWarehouse, warehouses }) => {
|
||||||
this.warehouses = warehouses.results
|
this.warehouses = warehouses.results
|
||||||
this.documentForm.get('warehouses').setValue(newWarehouse.id)
|
this.documentForm.get('warehouse').setValue(newWarehouse.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
</pngx-filterable-dropdown>
|
</pngx-filterable-dropdown>
|
||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Warehouse)) {
|
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Warehouse)) {
|
||||||
<pngx-filterable-dropdown title="Warehouses" icon="warehouse-fill" i18n-title
|
<pngx-filterable-dropdown title="Warehouse" icon="warehouse-fill" i18n-title
|
||||||
filterPlaceholder="Filter warehouses" i18n-filterPlaceholder
|
filterPlaceholder="Filter warehouses" i18n-filterPlaceholder
|
||||||
[items]="warehouses"
|
[items]="warehouses"
|
||||||
[disabled]="!userCanEditAll"
|
[disabled]="!userCanEditAll"
|
||||||
@ -83,7 +83,7 @@
|
|||||||
[manyToOne]="true"
|
[manyToOne]="true"
|
||||||
[applyOnClose]="applyOnClose"
|
[applyOnClose]="applyOnClose"
|
||||||
[createRef]="createWarehouse.bind(this)"
|
[createRef]="createWarehouse.bind(this)"
|
||||||
(opened)="openWarehousesDropdown()"
|
(opened)="openWarehouseDropdown()"
|
||||||
[(selectionModel)]="warehouseSelectionModel"
|
[(selectionModel)]="warehouseSelectionModel"
|
||||||
[documentCounts]="warehouseDocumentCounts"
|
[documentCounts]="warehouseDocumentCounts"
|
||||||
(apply)="setWarehouses($event)">
|
(apply)="setWarehouses($event)">
|
||||||
|
@ -65,7 +65,7 @@ export class BulkEditorComponent
|
|||||||
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
|
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
storagePathsSelectionModel = new FilterableDropdownSelectionModel()
|
storagePathsSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
warehousesSelectionModel = new FilterableDropdownSelectionModel()
|
warehouseSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
tagDocumentCounts: SelectionDataItem[]
|
tagDocumentCounts: SelectionDataItem[]
|
||||||
correspondentDocumentCounts: SelectionDataItem[]
|
correspondentDocumentCounts: SelectionDataItem[]
|
||||||
documentTypeDocumentCounts: SelectionDataItem[]
|
documentTypeDocumentCounts: SelectionDataItem[]
|
||||||
@ -324,7 +324,7 @@ export class BulkEditorComponent
|
|||||||
this.warehouseDocumentCounts = s.selected_warehouses
|
this.warehouseDocumentCounts = s.selected_warehouses
|
||||||
this.applySelectionData(
|
this.applySelectionData(
|
||||||
s.selected_warehouses,
|
s.selected_warehouses,
|
||||||
this.warehousesSelectionModel
|
this.warehouseSelectionModel
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -609,6 +609,27 @@ export class BulkEditorComponent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createWarehouse(name: string) {
|
||||||
|
let modal = this.modalService.open(WarehouseEditDialogComponent, {
|
||||||
|
backdrop: 'static',
|
||||||
|
})
|
||||||
|
modal.componentInstance.dialogMode = EditDialogMode.CREATE
|
||||||
|
modal.componentInstance.object = { name }
|
||||||
|
modal.componentInstance.succeeded
|
||||||
|
.pipe(
|
||||||
|
switchMap((newWarehouse) => {
|
||||||
|
return this.warehouseService
|
||||||
|
.listAll()
|
||||||
|
.pipe(map((warehouses) => ({ newWarehouse, warehouses })))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe(({ newWarehouse, warehouses }) => {
|
||||||
|
this.warehouses = warehouses.results
|
||||||
|
this.warehouseSelectionModel.toggle(newWarehouse.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
createDocumentType(name: string) {
|
createDocumentType(name: string) {
|
||||||
let modal = this.modalService.open(DocumentTypeEditDialogComponent, {
|
let modal = this.modalService.open(DocumentTypeEditDialogComponent, {
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
@ -651,26 +672,6 @@ export class BulkEditorComponent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
createWarehouse(name: string) {
|
|
||||||
let modal = this.modalService.open(WarehouseEditDialogComponent, {
|
|
||||||
backdrop: 'static',
|
|
||||||
})
|
|
||||||
modal.componentInstance.dialogMode = EditDialogMode.CREATE
|
|
||||||
modal.componentInstance.object = { name }
|
|
||||||
modal.componentInstance.succeeded
|
|
||||||
.pipe(
|
|
||||||
switchMap((newWarehouse) => {
|
|
||||||
return this.warehouseService
|
|
||||||
.listAll()
|
|
||||||
.pipe(map((warehouses) => ({ newWarehouse, warehouses })))
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
|
||||||
.subscribe(({ newWarehouse, warehouses }) => {
|
|
||||||
this.warehouses = warehouses.results
|
|
||||||
this.warehousesSelectionModel.toggle(newWarehouse.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
applyDelete() {
|
applyDelete() {
|
||||||
let modal = this.modalService.open(ConfirmDialogComponent, {
|
let modal = this.modalService.open(ConfirmDialogComponent, {
|
||||||
|
@ -83,10 +83,10 @@
|
|||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.storage_path$ | async)?.name}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.storage_path$ | async)?.name}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.warehouses) {
|
@if (document.warehouse) {
|
||||||
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by warehouse" i18n-title
|
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by warehouse" i18n-title
|
||||||
(click)="clickWarehouse.emit(document.warehouses);$event.stopPropagation()">
|
(click)="clickWarehouse.emit(document.warehouse);$event.stopPropagation()">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.warehouses$ | async)?.name}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.warehouse$ | async)?.name}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.archive_serial_number | isNumber) {
|
@if (document.archive_serial_number | isNumber) {
|
||||||
|
@ -47,15 +47,15 @@ export class DocumentCardLargeComponent extends ComponentWithPermissions {
|
|||||||
@Output()
|
@Output()
|
||||||
clickCorrespondent = new EventEmitter<number>()
|
clickCorrespondent = new EventEmitter<number>()
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
clickWarehouse = new EventEmitter<number>()
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
clickDocumentType = new EventEmitter<number>()
|
clickDocumentType = new EventEmitter<number>()
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
clickStoragePath = new EventEmitter<number>()
|
clickStoragePath = new EventEmitter<number>()
|
||||||
|
|
||||||
@Output()
|
|
||||||
clickWarehouse = new EventEmitter<number>()
|
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
clickMoreLike = new EventEmitter()
|
clickMoreLike = new EventEmitter()
|
||||||
|
|
||||||
|
@ -54,11 +54,11 @@
|
|||||||
<small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
|
<small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.warehouses) {
|
@if (document.warehouse) {
|
||||||
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle warehouse filter" i18n-title
|
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle warehouse filter" i18n-title
|
||||||
(click)="clickWarehouse.emit(document.warehouses);$event.stopPropagation()">
|
(click)="clickWarehouse.emit(document.warehouse);$event.stopPropagation()">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
|
||||||
<small>{{(document.warehouses$ | async)?.name ?? privateName}}</small>
|
<small>{{(document.warehouse$ | async)?.name ?? privateName}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
|
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
|
||||||
|
@ -125,7 +125,7 @@
|
|||||||
@if (displayMode === 'largeCards') {
|
@if (displayMode === 'largeCards') {
|
||||||
<div>
|
<div>
|
||||||
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
||||||
<pngx-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickMoreLike)="clickMoreLike(d.id)">
|
<pngx-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickWarehouse)="clickWarehouse($event)" (clickMoreLike)="clickMoreLike(d.id)">
|
||||||
</pngx-document-card-large>
|
</pngx-document-card-large>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -271,8 +271,8 @@
|
|||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Warehouse)) {
|
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Warehouse)) {
|
||||||
<td class="d-none d-xl-table-cell">
|
<td class="d-none d-xl-table-cell">
|
||||||
@if (d.warehouses) {
|
@if (d.warehouse) {
|
||||||
<a (click)="clickWarehouse(d.warehouses);$event.stopPropagation()" title="Filter by warehouse" i18n-title>{{(d.warehouses$ | async)?.name}}</a>
|
<a (click)="clickWarehouse(d.warehouse);$event.stopPropagation()" title="Filter by warehouse" i18n-title>{{(d.warehouse$ | async)?.name}}</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
|
@ -263,6 +263,9 @@ describe('FilterEditorComponent', () => {
|
|||||||
httpTestingController.expectNone(
|
httpTestingController.expectNone(
|
||||||
`${environment.apiBaseUrl}documents/storage_paths/`
|
`${environment.apiBaseUrl}documents/storage_paths/`
|
||||||
)
|
)
|
||||||
|
httpTestingController.expectNone(
|
||||||
|
`${environment.apiBaseUrl}documents/warehouses/`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// SET filterRules
|
// SET filterRules
|
||||||
@ -1807,6 +1810,10 @@ describe('FilterEditorComponent', () => {
|
|||||||
{ id: 32, document_count: 1 },
|
{ id: 32, document_count: 1 },
|
||||||
{ id: 33, document_count: 0 },
|
{ id: 33, document_count: 0 },
|
||||||
],
|
],
|
||||||
|
selected_warehouses: [
|
||||||
|
{ id: 42, document_count: 1 },
|
||||||
|
{ id: 43, document_count: 0 },
|
||||||
|
],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1865,6 +1872,24 @@ describe('FilterEditorComponent', () => {
|
|||||||
]
|
]
|
||||||
expect(component.generateFilterName()).toEqual('Without storage path')
|
expect(component.generateFilterName()).toEqual('Without storage path')
|
||||||
|
|
||||||
|
component.filterRules = [
|
||||||
|
{
|
||||||
|
rule_type: FILTER_HAS_WAREHOUSE_ANY,
|
||||||
|
value: '42',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
expect(component.generateFilterName()).toEqual(
|
||||||
|
`Warehouse path: ${warehouses[0].name}`
|
||||||
|
)
|
||||||
|
|
||||||
|
component.filterRules = [
|
||||||
|
{
|
||||||
|
rule_type: FILTER_WAREHOUSE,
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
expect(component.generateFilterName()).toEqual('Without warehouse')
|
||||||
|
|
||||||
component.filterRules = [
|
component.filterRules = [
|
||||||
{
|
{
|
||||||
rule_type: FILTER_HAS_TAGS_ALL,
|
rule_type: FILTER_HAS_TAGS_ALL,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
|
import { FILTER_HAS_WAREHOUSE_ANY } from 'src/app/data/filter-rule-type'
|
||||||
import { Warehouse } from 'src/app/data/warehouse'
|
import { Warehouse } from 'src/app/data/warehouse'
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
import {
|
import {
|
||||||
@ -32,7 +32,7 @@ export class WarehouseListComponent extends ManagementListComponent<Warehouse> {
|
|||||||
toastService,
|
toastService,
|
||||||
documentListViewService,
|
documentListViewService,
|
||||||
permissionsService,
|
permissionsService,
|
||||||
FILTER_HAS_TAGS_ALL,
|
FILTER_HAS_WAREHOUSE_ANY,
|
||||||
$localize`warehouse`,
|
$localize`warehouse`,
|
||||||
$localize`warehouses`,
|
$localize`warehouses`,
|
||||||
PermissionType.Warehouse,
|
PermissionType.Warehouse,
|
||||||
|
@ -29,9 +29,9 @@ export interface Document extends ObjectWithPermissions {
|
|||||||
|
|
||||||
storage_path?: number
|
storage_path?: number
|
||||||
|
|
||||||
warehouses$?: Observable<Warehouse>
|
warehouse$?: Observable<Warehouse>
|
||||||
|
|
||||||
warehouses?: number
|
warehouse?: number
|
||||||
|
|
||||||
title?: string
|
title?: string
|
||||||
|
|
||||||
|
@ -114,20 +114,20 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: FILTER_WAREHOUSE,
|
id: FILTER_WAREHOUSE,
|
||||||
filtervar: 'warehouses__id',
|
filtervar: 'warehouse__id',
|
||||||
isnull_filtervar: 'warehouses__isnull',
|
isnull_filtervar: 'warehouse__isnull',
|
||||||
datatype: 'warehouse',
|
datatype: 'warehouse',
|
||||||
multi: false,
|
multi: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: FILTER_HAS_WAREHOUSE_ANY,
|
id: FILTER_HAS_WAREHOUSE_ANY,
|
||||||
filtervar: 'warehouses__id__in',
|
filtervar: 'warehouse__id__in',
|
||||||
datatype: 'warehouse',
|
datatype: 'warehouse',
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: FILTER_DOES_NOT_HAVE_WAREHOUSE,
|
id: FILTER_DOES_NOT_HAVE_WAREHOUSE,
|
||||||
filtervar: 'warehouses__id__none',
|
filtervar: 'warehouse__id__none',
|
||||||
datatype: 'warehouse',
|
datatype: 'warehouse',
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
@ -67,6 +67,8 @@ export interface MailRule extends ObjectWithPermissions {
|
|||||||
|
|
||||||
assign_document_type?: number // PaperlessDocumentType.id
|
assign_document_type?: number // PaperlessDocumentType.id
|
||||||
|
|
||||||
|
assign_warehouse?: number // PaperlessWarehouse.id
|
||||||
|
|
||||||
assign_correspondent_from?: MailMetadataCorrespondentOption
|
assign_correspondent_from?: MailMetadataCorrespondentOption
|
||||||
|
|
||||||
assign_correspondent?: number // PaperlessCorrespondent.id
|
assign_correspondent?: number // PaperlessCorrespondent.id
|
||||||
|
@ -5,4 +5,6 @@ export interface Warehouse extends MatchingModel {
|
|||||||
type?: string
|
type?: string
|
||||||
|
|
||||||
parent_warehouse?: number
|
parent_warehouse?: number
|
||||||
|
|
||||||
|
path?: string
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export interface WorkflowAction extends ObjectWithId {
|
|||||||
|
|
||||||
assign_storage_path?: number // StoragePath.id
|
assign_storage_path?: number // StoragePath.id
|
||||||
|
|
||||||
assign_warehouses?: number // Warehouse.id
|
assign_warehouse?: number // Warehouse.id
|
||||||
|
|
||||||
assign_owner?: number // User.id
|
assign_owner?: number // User.id
|
||||||
|
|
||||||
|
@ -34,4 +34,7 @@ export interface WorkflowTrigger extends ObjectWithId {
|
|||||||
filter_has_correspondent?: number // Correspondent.id
|
filter_has_correspondent?: number // Correspondent.id
|
||||||
|
|
||||||
filter_has_document_type?: number // DocumentType.id
|
filter_has_document_type?: number // DocumentType.id
|
||||||
|
|
||||||
|
filter_has_warehouse?: number // Warehouse.id
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ const documents = [
|
|||||||
correspondent: 11,
|
correspondent: 11,
|
||||||
document_type: 3,
|
document_type: 3,
|
||||||
storage_path: 8,
|
storage_path: 8,
|
||||||
|
warehouse: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
@ -20,6 +20,7 @@ const documents = [
|
|||||||
correspondent: 11,
|
correspondent: 11,
|
||||||
document_type: 3,
|
document_type: 3,
|
||||||
storage_path: 8,
|
storage_path: 8,
|
||||||
|
warehouse: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
|
@ -138,6 +138,7 @@ describe('PermissionsService', () => {
|
|||||||
'view_savedview',
|
'view_savedview',
|
||||||
'view_uisettings',
|
'view_uisettings',
|
||||||
'delete_storagepath',
|
'delete_storagepath',
|
||||||
|
'delete_warehouse',
|
||||||
'delete_frontendsettings',
|
'delete_frontendsettings',
|
||||||
'change_paperlesstask',
|
'change_paperlesstask',
|
||||||
'view_taskresult',
|
'view_taskresult',
|
||||||
@ -185,6 +186,7 @@ describe('PermissionsService', () => {
|
|||||||
'delete_document',
|
'delete_document',
|
||||||
'change_uisettings',
|
'change_uisettings',
|
||||||
'change_storagepath',
|
'change_storagepath',
|
||||||
|
'change_warehouse',
|
||||||
'change_document',
|
'change_document',
|
||||||
'delete_tokenproxy',
|
'delete_tokenproxy',
|
||||||
'change_note',
|
'change_note',
|
||||||
@ -210,6 +212,7 @@ describe('PermissionsService', () => {
|
|||||||
'change_tag',
|
'change_tag',
|
||||||
'change_chordcounter',
|
'change_chordcounter',
|
||||||
'add_storagepath',
|
'add_storagepath',
|
||||||
|
'add_warehouse',
|
||||||
'delete_group',
|
'delete_group',
|
||||||
'add_taskattributes',
|
'add_taskattributes',
|
||||||
'delete_mailaccount',
|
'delete_mailaccount',
|
||||||
@ -240,6 +243,7 @@ describe('PermissionsService', () => {
|
|||||||
'delete_taskresult',
|
'delete_taskresult',
|
||||||
'view_contenttype',
|
'view_contenttype',
|
||||||
'view_storagepath',
|
'view_storagepath',
|
||||||
|
'view_warehouse',
|
||||||
'add_permission',
|
'add_permission',
|
||||||
'change_userobjectpermission',
|
'change_userobjectpermission',
|
||||||
'delete_savedviewfilterrule',
|
'delete_savedviewfilterrule',
|
||||||
|
@ -24,6 +24,7 @@ const documents = [
|
|||||||
correspondent: 11,
|
correspondent: 11,
|
||||||
document_type: 3,
|
document_type: 3,
|
||||||
storage_path: 8,
|
storage_path: 8,
|
||||||
|
warehouse: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@ -225,6 +226,7 @@ describe(`DocumentService`, () => {
|
|||||||
expect(doc.document_type$).not.toBeNull()
|
expect(doc.document_type$).not.toBeNull()
|
||||||
expect(doc.tags$).not.toBeNull()
|
expect(doc.tags$).not.toBeNull()
|
||||||
expect(doc.storage_path$).not.toBeNull()
|
expect(doc.storage_path$).not.toBeNull()
|
||||||
|
expect(doc.warehouse$).not.toBeNull()
|
||||||
})
|
})
|
||||||
httpTestingController
|
httpTestingController
|
||||||
.expectOne(
|
.expectOne(
|
||||||
|
@ -28,7 +28,7 @@ export const DOCUMENT_SORT_FIELDS = [
|
|||||||
{ field: 'correspondent__name', name: $localize`Correspondent` },
|
{ field: 'correspondent__name', name: $localize`Correspondent` },
|
||||||
{ field: 'title', name: $localize`Title` },
|
{ field: 'title', name: $localize`Title` },
|
||||||
{ field: 'document_type__name', name: $localize`Document type` },
|
{ field: 'document_type__name', name: $localize`Document type` },
|
||||||
{ field: 'warehouses__name', name: $localize`Warehouse` },
|
{ field: 'warehouse__name', name: $localize`Warehouse` },
|
||||||
{ field: 'created', name: $localize`Created` },
|
{ field: 'created', name: $localize`Created` },
|
||||||
{ field: 'added', name: $localize`Added` },
|
{ field: 'added', name: $localize`Added` },
|
||||||
{ field: 'modified', name: $localize`Modified` },
|
{ field: 'modified', name: $localize`Modified` },
|
||||||
@ -123,13 +123,13 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
|||||||
doc.storage_path$ = this.storagePathService.getCached(doc.storage_path)
|
doc.storage_path$ = this.storagePathService.getCached(doc.storage_path)
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
doc.warehouses &&
|
doc.warehouse &&
|
||||||
this.permissionsService.currentUserCan(
|
this.permissionsService.currentUserCan(
|
||||||
PermissionAction.View,
|
PermissionAction.View,
|
||||||
PermissionType.Warehouse
|
PermissionType.Warehouse
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
doc.warehouses$ = this.warehouseService.getCached(doc.warehouses)
|
doc.warehouse$ = this.warehouseService.getCached(doc.warehouse)
|
||||||
}
|
}
|
||||||
return doc
|
return doc
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ const group = {
|
|||||||
'view_savedview',
|
'view_savedview',
|
||||||
'view_uisettings',
|
'view_uisettings',
|
||||||
'delete_storagepath',
|
'delete_storagepath',
|
||||||
|
'delete_warehouse',
|
||||||
'delete_frontendsettings',
|
'delete_frontendsettings',
|
||||||
'change_paperlesstask',
|
'change_paperlesstask',
|
||||||
'view_taskresult',
|
'view_taskresult',
|
||||||
@ -91,6 +92,7 @@ const group = {
|
|||||||
'delete_document',
|
'delete_document',
|
||||||
'change_uisettings',
|
'change_uisettings',
|
||||||
'change_storagepath',
|
'change_storagepath',
|
||||||
|
'change_warehouse',
|
||||||
'change_document',
|
'change_document',
|
||||||
'delete_tokenproxy',
|
'delete_tokenproxy',
|
||||||
'change_note',
|
'change_note',
|
||||||
@ -116,6 +118,7 @@ const group = {
|
|||||||
'change_tag',
|
'change_tag',
|
||||||
'change_chordcounter',
|
'change_chordcounter',
|
||||||
'add_storagepath',
|
'add_storagepath',
|
||||||
|
'add_warehouse',
|
||||||
'delete_group',
|
'delete_group',
|
||||||
'add_taskattributes',
|
'add_taskattributes',
|
||||||
'delete_mailaccount',
|
'delete_mailaccount',
|
||||||
@ -146,6 +149,7 @@ const group = {
|
|||||||
'delete_taskresult',
|
'delete_taskresult',
|
||||||
'view_contenttype',
|
'view_contenttype',
|
||||||
'view_storagepath',
|
'view_storagepath',
|
||||||
|
'view_warehouse',
|
||||||
'add_permission',
|
'add_permission',
|
||||||
'change_userobjectpermission',
|
'change_userobjectpermission',
|
||||||
'delete_savedviewfilterrule',
|
'delete_savedviewfilterrule',
|
||||||
|
@ -13,6 +13,7 @@ from documents.models import SavedView
|
|||||||
from documents.models import SavedViewFilterRule
|
from documents.models import SavedViewFilterRule
|
||||||
from documents.models import ShareLink
|
from documents.models import ShareLink
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
|
from documents.models import Warehouse
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if settings.AUDIT_LOG_ENABLED:
|
||||||
@ -38,6 +39,10 @@ class DocumentTypeAdmin(GuardedModelAdmin):
|
|||||||
list_filter = ("matching_algorithm",)
|
list_filter = ("matching_algorithm",)
|
||||||
list_editable = ("match", "matching_algorithm")
|
list_editable = ("match", "matching_algorithm")
|
||||||
|
|
||||||
|
class WarehouseAdmin(GuardedModelAdmin):
|
||||||
|
list_display = ("name", "type", "path", "parent_warehouse", "match", "matching_algorithm")
|
||||||
|
list_filter = ("matching_algorithm",)
|
||||||
|
list_editable = ("match", "matching_algorithm")
|
||||||
|
|
||||||
class DocumentAdmin(GuardedModelAdmin):
|
class DocumentAdmin(GuardedModelAdmin):
|
||||||
search_fields = ("correspondent__name", "title", "content", "tags__name")
|
search_fields = ("correspondent__name", "title", "content", "tags__name")
|
||||||
@ -188,6 +193,7 @@ class CustomFieldInstancesAdmin(GuardedModelAdmin):
|
|||||||
admin.site.register(Correspondent, CorrespondentAdmin)
|
admin.site.register(Correspondent, CorrespondentAdmin)
|
||||||
admin.site.register(Tag, TagAdmin)
|
admin.site.register(Tag, TagAdmin)
|
||||||
admin.site.register(DocumentType, DocumentTypeAdmin)
|
admin.site.register(DocumentType, DocumentTypeAdmin)
|
||||||
|
admin.site.register(Warehouse, WarehouseAdmin)
|
||||||
admin.site.register(Document, DocumentAdmin)
|
admin.site.register(Document, DocumentAdmin)
|
||||||
admin.site.register(SavedView, SavedViewAdmin)
|
admin.site.register(SavedView, SavedViewAdmin)
|
||||||
admin.site.register(StoragePath, StoragePathAdmin)
|
admin.site.register(StoragePath, StoragePathAdmin)
|
||||||
|
@ -15,6 +15,7 @@ class DocumentsConfig(AppConfig):
|
|||||||
from documents.signals.handlers import run_workflow_added
|
from documents.signals.handlers import run_workflow_added
|
||||||
from documents.signals.handlers import run_workflow_updated
|
from documents.signals.handlers import run_workflow_updated
|
||||||
from documents.signals.handlers import set_correspondent
|
from documents.signals.handlers import set_correspondent
|
||||||
|
from documents.signals.handlers import set_warehouse
|
||||||
from documents.signals.handlers import set_document_type
|
from documents.signals.handlers import set_document_type
|
||||||
from documents.signals.handlers import set_log_entry
|
from documents.signals.handlers import set_log_entry
|
||||||
from documents.signals.handlers import set_storage_path
|
from documents.signals.handlers import set_storage_path
|
||||||
@ -22,6 +23,7 @@ class DocumentsConfig(AppConfig):
|
|||||||
|
|
||||||
document_consumption_finished.connect(add_inbox_tags)
|
document_consumption_finished.connect(add_inbox_tags)
|
||||||
document_consumption_finished.connect(set_correspondent)
|
document_consumption_finished.connect(set_correspondent)
|
||||||
|
document_consumption_finished.connect(set_warehouse)
|
||||||
document_consumption_finished.connect(set_document_type)
|
document_consumption_finished.connect(set_document_type)
|
||||||
document_consumption_finished.connect(set_tags)
|
document_consumption_finished.connect(set_tags)
|
||||||
document_consumption_finished.connect(set_storage_path)
|
document_consumption_finished.connect(set_storage_path)
|
||||||
|
@ -15,6 +15,7 @@ from documents.models import Correspondent
|
|||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
|
from documents.models import Warehouse
|
||||||
from documents.permissions import set_permissions_for_object
|
from documents.permissions import set_permissions_for_object
|
||||||
from documents.tasks import bulk_update_documents
|
from documents.tasks import bulk_update_documents
|
||||||
from documents.tasks import consume_file
|
from documents.tasks import consume_file
|
||||||
@ -52,6 +53,22 @@ def set_storage_path(doc_ids, storage_path):
|
|||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|
||||||
|
def set_warehouse(doc_ids, warehouse):
|
||||||
|
if warehouse:
|
||||||
|
warehouse = Warehouse.objects.get(id=warehouse)
|
||||||
|
|
||||||
|
qs = Document.objects.filter(
|
||||||
|
Q(id__in=doc_ids) & ~Q(warehouse=warehouse),
|
||||||
|
)
|
||||||
|
affected_docs = [doc.id for doc in qs]
|
||||||
|
qs.update(warehouse=warehouse)
|
||||||
|
|
||||||
|
bulk_update_documents.delay(
|
||||||
|
document_ids=affected_docs,
|
||||||
|
)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
|
||||||
|
|
||||||
def set_document_type(doc_ids, document_type):
|
def set_document_type(doc_ids, document_type):
|
||||||
if document_type:
|
if document_type:
|
||||||
|
@ -86,6 +86,7 @@ class DocumentClassifier:
|
|||||||
self.tags_binarizer = None
|
self.tags_binarizer = None
|
||||||
self.tags_classifier = None
|
self.tags_classifier = None
|
||||||
self.correspondent_classifier = None
|
self.correspondent_classifier = None
|
||||||
|
self.warehouse_classifier = None
|
||||||
self.document_type_classifier = None
|
self.document_type_classifier = None
|
||||||
self.storage_path_classifier = None
|
self.storage_path_classifier = None
|
||||||
|
|
||||||
@ -112,6 +113,7 @@ class DocumentClassifier:
|
|||||||
|
|
||||||
self.tags_classifier = pickle.load(f)
|
self.tags_classifier = pickle.load(f)
|
||||||
self.correspondent_classifier = pickle.load(f)
|
self.correspondent_classifier = pickle.load(f)
|
||||||
|
self.warehouse_classifier = pickle.load(f)
|
||||||
self.document_type_classifier = pickle.load(f)
|
self.document_type_classifier = pickle.load(f)
|
||||||
self.storage_path_classifier = pickle.load(f)
|
self.storage_path_classifier = pickle.load(f)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@ -148,6 +150,7 @@ class DocumentClassifier:
|
|||||||
pickle.dump(self.tags_classifier, f)
|
pickle.dump(self.tags_classifier, f)
|
||||||
|
|
||||||
pickle.dump(self.correspondent_classifier, f)
|
pickle.dump(self.correspondent_classifier, f)
|
||||||
|
pickle.dump(self.warehouse_classifier, f)
|
||||||
pickle.dump(self.document_type_classifier, f)
|
pickle.dump(self.document_type_classifier, f)
|
||||||
pickle.dump(self.storage_path_classifier, f)
|
pickle.dump(self.storage_path_classifier, f)
|
||||||
|
|
||||||
@ -165,6 +168,7 @@ class DocumentClassifier:
|
|||||||
|
|
||||||
labels_tags = []
|
labels_tags = []
|
||||||
labels_correspondent = []
|
labels_correspondent = []
|
||||||
|
labels_warehouse = []
|
||||||
labels_document_type = []
|
labels_document_type = []
|
||||||
labels_storage_path = []
|
labels_storage_path = []
|
||||||
|
|
||||||
@ -186,6 +190,13 @@ class DocumentClassifier:
|
|||||||
hasher.update(y.to_bytes(4, "little", signed=True))
|
hasher.update(y.to_bytes(4, "little", signed=True))
|
||||||
labels_correspondent.append(y)
|
labels_correspondent.append(y)
|
||||||
|
|
||||||
|
y = -1
|
||||||
|
wh = doc.warehouse
|
||||||
|
if wh and wh.matching_algorithm == MatchingModel.MATCH_AUTO:
|
||||||
|
y = wh.pk
|
||||||
|
hasher.update(y.to_bytes(4, "little", signed=True))
|
||||||
|
labels_warehouse.append(y)
|
||||||
|
|
||||||
tags = sorted(
|
tags = sorted(
|
||||||
tag.pk
|
tag.pk
|
||||||
for tag in doc.tags.filter(
|
for tag in doc.tags.filter(
|
||||||
@ -234,10 +245,11 @@ class DocumentClassifier:
|
|||||||
# it usually is.
|
# it usually is.
|
||||||
num_correspondents = len(set(labels_correspondent) | {-1}) - 1
|
num_correspondents = len(set(labels_correspondent) | {-1}) - 1
|
||||||
num_document_types = len(set(labels_document_type) | {-1}) - 1
|
num_document_types = len(set(labels_document_type) | {-1}) - 1
|
||||||
|
num_warehouses = len(set(labels_warehouse) | {-1}) - 1
|
||||||
num_storage_paths = len(set(labels_storage_path) | {-1}) - 1
|
num_storage_paths = len(set(labels_storage_path) | {-1}) - 1
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{docs_queryset.count()} documents, {num_tags} tag(s), {num_correspondents} correspondent(s), "
|
f"{docs_queryset.count()} documents, {num_tags} tag(s), {num_correspondents} correspondent(s), {num_warehouses} warehouse(s) "
|
||||||
f"{num_document_types} document type(s). {num_storage_paths} storage path(es)",
|
f"{num_document_types} document type(s). {num_storage_paths} storage path(es)",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -304,6 +316,17 @@ class DocumentClassifier:
|
|||||||
"classifier.",
|
"classifier.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if num_warehouses > 0:
|
||||||
|
logger.debug("Training warehouse classifier...")
|
||||||
|
self.warehouse_classifier = MLPClassifier(tol=0.01)
|
||||||
|
self.warehouse_classifier.fit(data_vectorized, labels_warehouse)
|
||||||
|
else:
|
||||||
|
self.warehouse_classifier = None
|
||||||
|
logger.debug(
|
||||||
|
"There are no warehouses. Not training warehouse "
|
||||||
|
"classifier.",
|
||||||
|
)
|
||||||
|
|
||||||
if num_document_types > 0:
|
if num_document_types > 0:
|
||||||
logger.debug("Training document type classifier...")
|
logger.debug("Training document type classifier...")
|
||||||
self.document_type_classifier = MLPClassifier(tol=0.01)
|
self.document_type_classifier = MLPClassifier(tol=0.01)
|
||||||
@ -415,6 +438,17 @@ class DocumentClassifier:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def predict_warehouse(self, content: str) -> Optional[int]:
|
||||||
|
if self.warehouse_classifier:
|
||||||
|
X = self.data_vectorizer.transform([self.preprocess_content(content)])
|
||||||
|
warehouse_id = self.warehouse_classifier.predict(X)
|
||||||
|
if warehouse_id != -1:
|
||||||
|
return warehouse_id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def predict_document_type(self, content: str) -> Optional[int]:
|
def predict_document_type(self, content: str) -> Optional[int]:
|
||||||
if self.document_type_classifier:
|
if self.document_type_classifier:
|
||||||
X = self.data_vectorizer.transform([self.preprocess_content(content)])
|
X = self.data_vectorizer.transform([self.preprocess_content(content)])
|
||||||
|
@ -32,6 +32,7 @@ from documents.models import Document
|
|||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import FileInfo
|
from documents.models import FileInfo
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
|
from documents.models import Warehouse
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.models import Workflow
|
from documents.models import Workflow
|
||||||
from documents.models import WorkflowAction
|
from documents.models import WorkflowAction
|
||||||
@ -76,6 +77,7 @@ class WorkflowTriggerPlugin(
|
|||||||
.prefetch_related("actions__assign_custom_fields")
|
.prefetch_related("actions__assign_custom_fields")
|
||||||
.prefetch_related("actions__remove_tags")
|
.prefetch_related("actions__remove_tags")
|
||||||
.prefetch_related("actions__remove_correspondents")
|
.prefetch_related("actions__remove_correspondents")
|
||||||
|
.prefetch_related("actions__remove_warehouses")
|
||||||
.prefetch_related("actions__remove_document_types")
|
.prefetch_related("actions__remove_document_types")
|
||||||
.prefetch_related("actions__remove_storage_paths")
|
.prefetch_related("actions__remove_storage_paths")
|
||||||
.prefetch_related("actions__remove_custom_fields")
|
.prefetch_related("actions__remove_custom_fields")
|
||||||
@ -110,6 +112,10 @@ class WorkflowTriggerPlugin(
|
|||||||
action_overrides.document_type_id = (
|
action_overrides.document_type_id = (
|
||||||
action.assign_document_type.pk
|
action.assign_document_type.pk
|
||||||
)
|
)
|
||||||
|
if action.assign_warehouse is not None:
|
||||||
|
action_overrides.warehouse_id = (
|
||||||
|
action.assign_warehouse.pk
|
||||||
|
)
|
||||||
if action.assign_storage_path is not None:
|
if action.assign_storage_path is not None:
|
||||||
action_overrides.storage_path_id = (
|
action_overrides.storage_path_id = (
|
||||||
action.assign_storage_path.pk
|
action.assign_storage_path.pk
|
||||||
@ -298,6 +304,7 @@ class Consumer(LoggingMixin):
|
|||||||
self.filename = None
|
self.filename = None
|
||||||
self.override_title = None
|
self.override_title = None
|
||||||
self.override_correspondent_id = None
|
self.override_correspondent_id = None
|
||||||
|
self.override_warehouse_id = None
|
||||||
self.override_tag_ids = None
|
self.override_tag_ids = None
|
||||||
self.override_document_type_id = None
|
self.override_document_type_id = None
|
||||||
self.override_asn = None
|
self.override_asn = None
|
||||||
@ -494,6 +501,7 @@ class Consumer(LoggingMixin):
|
|||||||
override_correspondent_id=None,
|
override_correspondent_id=None,
|
||||||
override_document_type_id=None,
|
override_document_type_id=None,
|
||||||
override_tag_ids=None,
|
override_tag_ids=None,
|
||||||
|
override_warehouse_id=None,
|
||||||
override_storage_path_id=None,
|
override_storage_path_id=None,
|
||||||
task_id=None,
|
task_id=None,
|
||||||
override_created=None,
|
override_created=None,
|
||||||
@ -515,6 +523,7 @@ class Consumer(LoggingMixin):
|
|||||||
self.override_correspondent_id = override_correspondent_id
|
self.override_correspondent_id = override_correspondent_id
|
||||||
self.override_document_type_id = override_document_type_id
|
self.override_document_type_id = override_document_type_id
|
||||||
self.override_tag_ids = override_tag_ids
|
self.override_tag_ids = override_tag_ids
|
||||||
|
self.override_warehouse_id = override_warehouse_id
|
||||||
self.override_storage_path_id = override_storage_path_id
|
self.override_storage_path_id = override_storage_path_id
|
||||||
self.task_id = task_id or str(uuid.uuid4())
|
self.task_id = task_id or str(uuid.uuid4())
|
||||||
self.override_created = override_created
|
self.override_created = override_created
|
||||||
@ -874,6 +883,11 @@ class Consumer(LoggingMixin):
|
|||||||
pk=self.override_storage_path_id,
|
pk=self.override_storage_path_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.override_warehouse_id:
|
||||||
|
document.warehouse = Warehouse.objects.get(
|
||||||
|
pk=self.override_warehouse_id,
|
||||||
|
)
|
||||||
|
|
||||||
if self.override_asn:
|
if self.override_asn:
|
||||||
document.archive_serial_number = self.override_asn
|
document.archive_serial_number = self.override_asn
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ class DocumentMetadataOverrides:
|
|||||||
document_type_id: Optional[int] = None
|
document_type_id: Optional[int] = None
|
||||||
tag_ids: Optional[list[int]] = None
|
tag_ids: Optional[list[int]] = None
|
||||||
storage_path_id: Optional[int] = None
|
storage_path_id: Optional[int] = None
|
||||||
|
warehouse_id: Optional[int] = None
|
||||||
created: Optional[datetime.datetime] = None
|
created: Optional[datetime.datetime] = None
|
||||||
asn: Optional[int] = None
|
asn: Optional[int] = None
|
||||||
owner_id: Optional[int] = None
|
owner_id: Optional[int] = None
|
||||||
@ -48,6 +49,8 @@ class DocumentMetadataOverrides:
|
|||||||
self.document_type_id = other.document_type_id
|
self.document_type_id = other.document_type_id
|
||||||
if other.storage_path_id is not None:
|
if other.storage_path_id is not None:
|
||||||
self.storage_path_id = other.storage_path_id
|
self.storage_path_id = other.storage_path_id
|
||||||
|
if other.warehouse_id is not None:
|
||||||
|
self.warehouse_id = other.warehouse_id
|
||||||
if other.owner_id is not None:
|
if other.owner_id is not None:
|
||||||
self.owner_id = other.owner_id
|
self.owner_id = other.owner_id
|
||||||
|
|
||||||
@ -100,6 +103,7 @@ class DocumentMetadataOverrides:
|
|||||||
overrides.correspondent_id = doc.correspondent.id if doc.correspondent else None
|
overrides.correspondent_id = doc.correspondent.id if doc.correspondent else None
|
||||||
overrides.document_type_id = doc.document_type.id if doc.document_type else None
|
overrides.document_type_id = doc.document_type.id if doc.document_type else None
|
||||||
overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None
|
overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None
|
||||||
|
overrides.warehouse_id = doc.warehouse.id if doc.warehouse else None
|
||||||
overrides.owner_id = doc.owner.id if doc.owner else None
|
overrides.owner_id = doc.owner.id if doc.owner else None
|
||||||
overrides.tag_ids = list(doc.tags.values_list("id", flat=True))
|
overrides.tag_ids = list(doc.tags.values_list("id", flat=True))
|
||||||
|
|
||||||
|
@ -175,6 +175,14 @@ def generate_filename(
|
|||||||
else:
|
else:
|
||||||
document_type = no_value_default
|
document_type = no_value_default
|
||||||
|
|
||||||
|
if doc.warehouse:
|
||||||
|
warehouse = pathvalidate.sanitize_filename(
|
||||||
|
doc.warehouse.name,
|
||||||
|
replacement_text="-",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
warehouse = no_value_default
|
||||||
|
|
||||||
if doc.archive_serial_number:
|
if doc.archive_serial_number:
|
||||||
asn = str(doc.archive_serial_number)
|
asn = str(doc.archive_serial_number)
|
||||||
else:
|
else:
|
||||||
@ -199,6 +207,7 @@ def generate_filename(
|
|||||||
title=pathvalidate.sanitize_filename(doc.title, replacement_text="-"),
|
title=pathvalidate.sanitize_filename(doc.title, replacement_text="-"),
|
||||||
correspondent=correspondent,
|
correspondent=correspondent,
|
||||||
document_type=document_type,
|
document_type=document_type,
|
||||||
|
warehouse=warehouse,
|
||||||
created=local_created.isoformat(),
|
created=local_created.isoformat(),
|
||||||
created_year=local_created.strftime("%Y"),
|
created_year=local_created.strftime("%Y"),
|
||||||
created_year_short=local_created.strftime("%y"),
|
created_year_short=local_created.strftime("%y"),
|
||||||
|
@ -192,7 +192,7 @@ class DocumentFilterSet(FilterSet):
|
|||||||
|
|
||||||
storage_path__id__none = ObjectFilter(field_name="storage_path", exclude=True)
|
storage_path__id__none = ObjectFilter(field_name="storage_path", exclude=True)
|
||||||
|
|
||||||
warehouses__id__none = ObjectFilter(field_name="warehouses", exclude=True)
|
warehouse__id__none = ObjectFilter(field_name="warehouse", exclude=True)
|
||||||
|
|
||||||
is_in_inbox = InboxFilter()
|
is_in_inbox = InboxFilter()
|
||||||
|
|
||||||
@ -227,9 +227,9 @@ class DocumentFilterSet(FilterSet):
|
|||||||
"storage_path": ["isnull"],
|
"storage_path": ["isnull"],
|
||||||
"storage_path__id": ID_KWARGS,
|
"storage_path__id": ID_KWARGS,
|
||||||
"storage_path__name": CHAR_KWARGS,
|
"storage_path__name": CHAR_KWARGS,
|
||||||
"warehouses": ["isnull"],
|
"warehouse": ["isnull"],
|
||||||
"warehouses__id": ID_KWARGS,
|
"warehouse__id": ID_KWARGS,
|
||||||
"warehouses__name": CHAR_KWARGS,
|
"warehouse__name": CHAR_KWARGS,
|
||||||
"owner": ["isnull"],
|
"owner": ["isnull"],
|
||||||
"owner__id": ID_KWARGS,
|
"owner__id": ID_KWARGS,
|
||||||
"custom_fields": ["icontains"],
|
"custom_fields": ["icontains"],
|
||||||
|
@ -60,6 +60,9 @@ def get_schema():
|
|||||||
type=TEXT(sortable=True),
|
type=TEXT(sortable=True),
|
||||||
type_id=NUMERIC(),
|
type_id=NUMERIC(),
|
||||||
has_type=BOOLEAN(),
|
has_type=BOOLEAN(),
|
||||||
|
warehouse=TEXT(sortable=True),
|
||||||
|
warehouse_id=NUMERIC(),
|
||||||
|
has_warehouse=BOOLEAN(),
|
||||||
created=DATETIME(sortable=True),
|
created=DATETIME(sortable=True),
|
||||||
modified=DATETIME(sortable=True),
|
modified=DATETIME(sortable=True),
|
||||||
added=DATETIME(sortable=True),
|
added=DATETIME(sortable=True),
|
||||||
@ -155,6 +158,9 @@ def update_document(writer: AsyncWriter, doc: Document):
|
|||||||
type=doc.document_type.name if doc.document_type else None,
|
type=doc.document_type.name if doc.document_type else None,
|
||||||
type_id=doc.document_type.id if doc.document_type else None,
|
type_id=doc.document_type.id if doc.document_type else None,
|
||||||
has_type=doc.document_type is not None,
|
has_type=doc.document_type is not None,
|
||||||
|
warehouse=doc.warehouse.name if doc.warehouse else None,
|
||||||
|
warehouse_id=doc.warehouse.id if doc.warehouse else None,
|
||||||
|
has_warehouse=doc.warehouse is not None,
|
||||||
created=doc.created,
|
created=doc.created,
|
||||||
added=doc.added,
|
added=doc.added,
|
||||||
asn=asn,
|
asn=asn,
|
||||||
@ -197,6 +203,7 @@ def remove_document_from_index(document: Document):
|
|||||||
class DelayedQuery:
|
class DelayedQuery:
|
||||||
param_map = {
|
param_map = {
|
||||||
"correspondent": ("correspondent", ["id", "id__in", "id__none", "isnull"]),
|
"correspondent": ("correspondent", ["id", "id__in", "id__none", "isnull"]),
|
||||||
|
"warehouse": ("warehouse", ["id", "id__in", "id__none", "isnull"]),
|
||||||
"document_type": ("type", ["id", "id__in", "id__none", "isnull"]),
|
"document_type": ("type", ["id", "id__in", "id__none", "isnull"]),
|
||||||
"storage_path": ("path", ["id", "id__in", "id__none", "isnull"]),
|
"storage_path": ("path", ["id", "id__in", "id__none", "isnull"]),
|
||||||
"owner": ("owner", ["id", "id__in", "id__none", "isnull"]),
|
"owner": ("owner", ["id", "id__in", "id__none", "isnull"]),
|
||||||
|
@ -7,6 +7,7 @@ from documents.classifier import DocumentClassifier
|
|||||||
from documents.data_models import ConsumableDocument
|
from documents.data_models import ConsumableDocument
|
||||||
from documents.data_models import DocumentSource
|
from documents.data_models import DocumentSource
|
||||||
from documents.models import Correspondent
|
from documents.models import Correspondent
|
||||||
|
from documents.models import Warehouse
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import MatchingModel
|
from documents.models import MatchingModel
|
||||||
@ -56,6 +57,28 @@ def match_correspondents(document: Document, classifier: DocumentClassifier, use
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def match_warehouses(document: Document, classifier: DocumentClassifier, user=None):
|
||||||
|
pred_id = classifier.predict_warehouse(document.content) if classifier else None
|
||||||
|
|
||||||
|
if user is None and document.owner is not None:
|
||||||
|
user = document.owner
|
||||||
|
|
||||||
|
if user is not None:
|
||||||
|
warehouses = get_objects_for_user_owner_aware(
|
||||||
|
user,
|
||||||
|
"documents.view_warehouse",
|
||||||
|
Warehouse,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
warehouses = Warehouse.objects.all()
|
||||||
|
|
||||||
|
return list(
|
||||||
|
filter(
|
||||||
|
lambda o: matches(o, document)
|
||||||
|
or (o.pk == pred_id and o.matching_algorithm == MatchingModel.MATCH_AUTO),
|
||||||
|
warehouses,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def match_document_types(document: Document, classifier: DocumentClassifier, user=None):
|
def match_document_types(document: Document, classifier: DocumentClassifier, user=None):
|
||||||
pred_id = classifier.predict_document_type(document.content) if classifier else None
|
pred_id = classifier.predict_document_type(document.content) if classifier else None
|
||||||
@ -357,6 +380,16 @@ def existing_document_matches_workflow(
|
|||||||
)
|
)
|
||||||
trigger_matched = False
|
trigger_matched = False
|
||||||
|
|
||||||
|
# Document warehouse vs trigger has_warehouse
|
||||||
|
if (
|
||||||
|
trigger.filter_has_warehouse is not None
|
||||||
|
and document.warehouse != trigger.filter_has_warehouse
|
||||||
|
):
|
||||||
|
reason = (
|
||||||
|
f"Document warehouse {document.warehouse} does not match {trigger.filter_has_warehouse}",
|
||||||
|
)
|
||||||
|
trigger_matched = False
|
||||||
|
|
||||||
# Document document_type vs trigger has_document_type
|
# Document document_type vs trigger has_document_type
|
||||||
if (
|
if (
|
||||||
trigger.filter_has_document_type is not None
|
trigger.filter_has_document_type is not None
|
||||||
|
18
src/documents/migrations/1052_warehouse_path.py
Normal file
18
src/documents/migrations/1052_warehouse_path.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2024-05-30 07:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('documents', '1051_alter_warehouse_options_warehouse_is_insensitive_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='warehouse',
|
||||||
|
name='path',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='path'),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2024-05-30 12:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('documents', '1052_warehouse_path'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='warehouses',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='document',
|
||||||
|
name='warehouse',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='documents', to='documents.warehouse', verbose_name='warehouse'),
|
||||||
|
),
|
||||||
|
]
|
@ -144,6 +144,7 @@ class Warehouse(MatchingModel):
|
|||||||
choices=TYPE_WAREHOUSE,
|
choices=TYPE_WAREHOUSE,
|
||||||
default=WAREHOUSE,)
|
default=WAREHOUSE,)
|
||||||
parent_warehouse = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True )
|
parent_warehouse = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True )
|
||||||
|
path = models.TextField(_("path"), null=True, blank=True)
|
||||||
|
|
||||||
class Meta(MatchingModel.Meta):
|
class Meta(MatchingModel.Meta):
|
||||||
verbose_name = _("warehouse")
|
verbose_name = _("warehouse")
|
||||||
@ -178,6 +179,15 @@ class Document(ModelWithOwner):
|
|||||||
verbose_name=_("storage path"),
|
verbose_name=_("storage path"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
warehouse = models.ForeignKey(
|
||||||
|
Warehouse,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
related_name="documents",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
verbose_name=_("warehouse"),
|
||||||
|
)
|
||||||
|
|
||||||
title = models.CharField(_("title"), max_length=128, blank=True, db_index=True)
|
title = models.CharField(_("title"), max_length=128, blank=True, db_index=True)
|
||||||
|
|
||||||
document_type = models.ForeignKey(
|
document_type = models.ForeignKey(
|
||||||
@ -207,15 +217,6 @@ class Document(ModelWithOwner):
|
|||||||
verbose_name=_("tags"),
|
verbose_name=_("tags"),
|
||||||
)
|
)
|
||||||
|
|
||||||
warehouses = models.ForeignKey(
|
|
||||||
Warehouse,
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
related_name="documents",
|
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
verbose_name=_("warehouses"),
|
|
||||||
)
|
|
||||||
|
|
||||||
checksum = models.CharField(
|
checksum = models.CharField(
|
||||||
_("checksum"),
|
_("checksum"),
|
||||||
max_length=32,
|
max_length=32,
|
||||||
|
@ -428,7 +428,7 @@ class TagsField(serializers.PrimaryKeyRelatedField):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Tag.objects.all()
|
return Tag.objects.all()
|
||||||
|
|
||||||
class WarehousesField(serializers.PrimaryKeyRelatedField):
|
class WarehouseField(serializers.PrimaryKeyRelatedField):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Warehouse.objects.all()
|
return Warehouse.objects.all()
|
||||||
|
|
||||||
@ -656,7 +656,7 @@ class DocumentSerializer(
|
|||||||
):
|
):
|
||||||
correspondent = CorrespondentField(allow_null=True)
|
correspondent = CorrespondentField(allow_null=True)
|
||||||
tags = TagsField(many=True)
|
tags = TagsField(many=True)
|
||||||
warehouses = WarehousesField(allow_null=True)
|
warehouse = WarehouseField(allow_null=True)
|
||||||
document_type = DocumentTypeField(allow_null=True)
|
document_type = DocumentTypeField(allow_null=True)
|
||||||
storage_path = StoragePathField(allow_null=True)
|
storage_path = StoragePathField(allow_null=True)
|
||||||
|
|
||||||
@ -775,10 +775,10 @@ class DocumentSerializer(
|
|||||||
"correspondent",
|
"correspondent",
|
||||||
"document_type",
|
"document_type",
|
||||||
"storage_path",
|
"storage_path",
|
||||||
|
"warehouse",
|
||||||
"title",
|
"title",
|
||||||
"content",
|
"content",
|
||||||
"tags",
|
"tags",
|
||||||
"warehouses",
|
|
||||||
"created",
|
"created",
|
||||||
"created_date",
|
"created_date",
|
||||||
"modified",
|
"modified",
|
||||||
@ -882,6 +882,7 @@ class BulkEditSerializer(
|
|||||||
"set_correspondent",
|
"set_correspondent",
|
||||||
"set_document_type",
|
"set_document_type",
|
||||||
"set_storage_path",
|
"set_storage_path",
|
||||||
|
"set_warehouse"
|
||||||
"add_tag",
|
"add_tag",
|
||||||
"remove_tag",
|
"remove_tag",
|
||||||
"modify_tags",
|
"modify_tags",
|
||||||
@ -916,6 +917,8 @@ class BulkEditSerializer(
|
|||||||
return bulk_edit.set_document_type
|
return bulk_edit.set_document_type
|
||||||
elif method == "set_storage_path":
|
elif method == "set_storage_path":
|
||||||
return bulk_edit.set_storage_path
|
return bulk_edit.set_storage_path
|
||||||
|
elif method == "set_warehouse":
|
||||||
|
return bulk_edit.set_warehouse
|
||||||
elif method == "add_tag":
|
elif method == "add_tag":
|
||||||
return bulk_edit.add_tag
|
return bulk_edit.add_tag
|
||||||
elif method == "remove_tag":
|
elif method == "remove_tag":
|
||||||
@ -971,6 +974,17 @@ class BulkEditSerializer(
|
|||||||
raise serializers.ValidationError("Correspondent does not exist")
|
raise serializers.ValidationError("Correspondent does not exist")
|
||||||
else:
|
else:
|
||||||
raise serializers.ValidationError("correspondent not specified")
|
raise serializers.ValidationError("correspondent not specified")
|
||||||
|
def _validate_parameters_warehouse(self, parameters):
|
||||||
|
if "warehouse" in parameters:
|
||||||
|
warehouse_id = parameters["warehouse"]
|
||||||
|
if warehouse_id is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
Warehouse.objects.get(id=warehouse_id)
|
||||||
|
except Warehouse.DoesNotExist:
|
||||||
|
raise serializers.ValidationError("Warehouse does not exist")
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError("warehouse not specified")
|
||||||
|
|
||||||
def _validate_storage_path(self, parameters):
|
def _validate_storage_path(self, parameters):
|
||||||
if "storage_path" in parameters:
|
if "storage_path" in parameters:
|
||||||
@ -1059,6 +1073,8 @@ class BulkEditSerializer(
|
|||||||
self._validate_parameters_modify_tags(parameters)
|
self._validate_parameters_modify_tags(parameters)
|
||||||
elif method == bulk_edit.set_storage_path:
|
elif method == bulk_edit.set_storage_path:
|
||||||
self._validate_storage_path(parameters)
|
self._validate_storage_path(parameters)
|
||||||
|
elif method == bulk_edit.set_warehouse:
|
||||||
|
self._validate_parameters_warehouse(parameters)
|
||||||
elif method == bulk_edit.set_permissions:
|
elif method == bulk_edit.set_permissions:
|
||||||
self._validate_parameters_set_permissions(parameters)
|
self._validate_parameters_set_permissions(parameters)
|
||||||
elif method == bulk_edit.rotate:
|
elif method == bulk_edit.rotate:
|
||||||
@ -1108,6 +1124,14 @@ class PostDocumentSerializer(serializers.Serializer):
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
warehouse = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=Warehouse.objects.all(),
|
||||||
|
label="Warehouse",
|
||||||
|
allow_null=True,
|
||||||
|
write_only=True,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
storage_path = serializers.PrimaryKeyRelatedField(
|
storage_path = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=StoragePath.objects.all(),
|
queryset=StoragePath.objects.all(),
|
||||||
label="Storage path",
|
label="Storage path",
|
||||||
@ -1169,6 +1193,12 @@ class PostDocumentSerializer(serializers.Serializer):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def validate_warehouse(self, warehouse):
|
||||||
|
if warehouse:
|
||||||
|
return warehouse.id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def validate_tags(self, tags):
|
def validate_tags(self, tags):
|
||||||
if tags:
|
if tags:
|
||||||
return [tag.id for tag in tags]
|
return [tag.id for tag in tags]
|
||||||
@ -1232,6 +1262,7 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
|||||||
title="title",
|
title="title",
|
||||||
correspondent="correspondent",
|
correspondent="correspondent",
|
||||||
document_type="document_type",
|
document_type="document_type",
|
||||||
|
warehouse="warehouse",
|
||||||
created="created",
|
created="created",
|
||||||
created_year="created_year",
|
created_year="created_year",
|
||||||
created_year_short="created_year_short",
|
created_year_short="created_year_short",
|
||||||
@ -1504,6 +1535,7 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer):
|
|||||||
"filter_has_tags",
|
"filter_has_tags",
|
||||||
"filter_has_correspondent",
|
"filter_has_correspondent",
|
||||||
"filter_has_document_type",
|
"filter_has_document_type",
|
||||||
|
"filter_has_warehouse",
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
@ -1540,6 +1572,8 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
assign_tags = TagsField(many=True, allow_null=True, required=False)
|
assign_tags = TagsField(many=True, allow_null=True, required=False)
|
||||||
assign_document_type = DocumentTypeField(allow_null=True, required=False)
|
assign_document_type = DocumentTypeField(allow_null=True, required=False)
|
||||||
assign_storage_path = StoragePathField(allow_null=True, required=False)
|
assign_storage_path = StoragePathField(allow_null=True, required=False)
|
||||||
|
assign_warehouse = WarehouseField(allow_null =True, required=False)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WorkflowAction
|
model = WorkflowAction
|
||||||
@ -1551,6 +1585,7 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
"assign_correspondent",
|
"assign_correspondent",
|
||||||
"assign_document_type",
|
"assign_document_type",
|
||||||
"assign_storage_path",
|
"assign_storage_path",
|
||||||
|
"assign_warehouse"
|
||||||
"assign_owner",
|
"assign_owner",
|
||||||
"assign_view_users",
|
"assign_view_users",
|
||||||
"assign_view_groups",
|
"assign_view_groups",
|
||||||
@ -1565,6 +1600,8 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
"remove_document_types",
|
"remove_document_types",
|
||||||
"remove_all_storage_paths",
|
"remove_all_storage_paths",
|
||||||
"remove_storage_paths",
|
"remove_storage_paths",
|
||||||
|
"remove_all_warehouses",
|
||||||
|
"remove_warehouses",
|
||||||
"remove_custom_fields",
|
"remove_custom_fields",
|
||||||
"remove_all_custom_fields",
|
"remove_all_custom_fields",
|
||||||
"remove_all_owners",
|
"remove_all_owners",
|
||||||
@ -1658,6 +1695,7 @@ class WorkflowSerializer(serializers.ModelSerializer):
|
|||||||
remove_correspondents = action.pop("remove_correspondents", None)
|
remove_correspondents = action.pop("remove_correspondents", None)
|
||||||
remove_document_types = action.pop("remove_document_types", None)
|
remove_document_types = action.pop("remove_document_types", None)
|
||||||
remove_storage_paths = action.pop("remove_storage_paths", None)
|
remove_storage_paths = action.pop("remove_storage_paths", None)
|
||||||
|
remove_warehouses = action.pop("remove_warehouses", None)
|
||||||
remove_custom_fields = action.pop("remove_custom_fields", None)
|
remove_custom_fields = action.pop("remove_custom_fields", None)
|
||||||
remove_owners = action.pop("remove_owners", None)
|
remove_owners = action.pop("remove_owners", None)
|
||||||
remove_view_users = action.pop("remove_view_users", None)
|
remove_view_users = action.pop("remove_view_users", None)
|
||||||
@ -1690,6 +1728,8 @@ class WorkflowSerializer(serializers.ModelSerializer):
|
|||||||
action_instance.remove_document_types.set(remove_document_types)
|
action_instance.remove_document_types.set(remove_document_types)
|
||||||
if remove_storage_paths is not None:
|
if remove_storage_paths is not None:
|
||||||
action_instance.remove_storage_paths.set(remove_storage_paths)
|
action_instance.remove_storage_paths.set(remove_storage_paths)
|
||||||
|
if remove_warehouses is not None:
|
||||||
|
action_instance.remove_warehouses.set(remove_warehouses)
|
||||||
if remove_custom_fields is not None:
|
if remove_custom_fields is not None:
|
||||||
action_instance.remove_custom_fields.set(remove_custom_fields)
|
action_instance.remove_custom_fields.set(remove_custom_fields)
|
||||||
if remove_owners is not None:
|
if remove_owners is not None:
|
||||||
@ -1756,22 +1796,12 @@ class WorkflowSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class WarehouseSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
class WarehouseSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
||||||
document_count = serializers.SerializerMethodField()
|
|
||||||
def get_document_count(self,obj):
|
|
||||||
document = Document.objects.filter(warehouses=obj).count()
|
|
||||||
return document
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Warehouse
|
model = Warehouse
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def to_representation(self, instance):
|
|
||||||
data = super().to_representation(instance)
|
|
||||||
if instance.parent_warehouse:
|
|
||||||
data['parent_warehouse'] = WarehouseSerializer(instance.parent_warehouse).data
|
|
||||||
else:
|
|
||||||
data['parent_warehouse'] = None
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -130,6 +130,59 @@ def set_correspondent(
|
|||||||
document.correspondent = selected
|
document.correspondent = selected
|
||||||
document.save(update_fields=("correspondent",))
|
document.save(update_fields=("correspondent",))
|
||||||
|
|
||||||
|
def set_warehouse(
|
||||||
|
sender,
|
||||||
|
document: Document,
|
||||||
|
logging_group=None,
|
||||||
|
classifier: Optional[DocumentClassifier] = None,
|
||||||
|
replace=False,
|
||||||
|
use_first=True,
|
||||||
|
suggest=False,
|
||||||
|
base_url=None,
|
||||||
|
stdout=None,
|
||||||
|
style_func=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
if document.warehouse and not replace:
|
||||||
|
return
|
||||||
|
|
||||||
|
potential_warehouses = matching.match_warehouses(document, classifier)
|
||||||
|
|
||||||
|
potential_count = len(potential_warehouses)
|
||||||
|
selected = potential_warehouses[0] if potential_warehouses else None
|
||||||
|
if potential_count > 1:
|
||||||
|
if use_first:
|
||||||
|
logger.debug(
|
||||||
|
f"Detected {potential_count} potential warehouses, "
|
||||||
|
f"so we've opted for {selected}",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.debug(
|
||||||
|
f"Detected {potential_count} potential warehouses, "
|
||||||
|
f"not assigning any warehouse",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected or replace:
|
||||||
|
if suggest:
|
||||||
|
_suggestion_printer(
|
||||||
|
stdout,
|
||||||
|
style_func,
|
||||||
|
"warehouse",
|
||||||
|
document,
|
||||||
|
selected,
|
||||||
|
base_url,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
f"Assigning warehouse {selected} to {document}",
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
|
||||||
|
document.warehouse = selected
|
||||||
|
document.save(update_fields=("warehouse",))
|
||||||
|
|
||||||
def set_document_type(
|
def set_document_type(
|
||||||
sender,
|
sender,
|
||||||
@ -545,6 +598,7 @@ def run_workflow(
|
|||||||
.prefetch_related("actions__assign_custom_fields")
|
.prefetch_related("actions__assign_custom_fields")
|
||||||
.prefetch_related("actions__remove_tags")
|
.prefetch_related("actions__remove_tags")
|
||||||
.prefetch_related("actions__remove_correspondents")
|
.prefetch_related("actions__remove_correspondents")
|
||||||
|
.prefetch_related("actions__remove_warehouses")
|
||||||
.prefetch_related("actions__remove_document_types")
|
.prefetch_related("actions__remove_document_types")
|
||||||
.prefetch_related("actions__remove_storage_paths")
|
.prefetch_related("actions__remove_storage_paths")
|
||||||
.prefetch_related("actions__remove_custom_fields")
|
.prefetch_related("actions__remove_custom_fields")
|
||||||
@ -571,6 +625,9 @@ def run_workflow(
|
|||||||
if action.assign_correspondent is not None:
|
if action.assign_correspondent is not None:
|
||||||
document.correspondent = action.assign_correspondent
|
document.correspondent = action.assign_correspondent
|
||||||
|
|
||||||
|
if action.assign_warehouse is not None:
|
||||||
|
document.warehouse = action.assign_warehouse
|
||||||
|
|
||||||
if action.assign_document_type is not None:
|
if action.assign_document_type is not None:
|
||||||
document.document_type = action.assign_document_type
|
document.document_type = action.assign_document_type
|
||||||
|
|
||||||
@ -594,6 +651,11 @@ def run_workflow(
|
|||||||
if document.document_type is not None
|
if document.document_type is not None
|
||||||
else ""
|
else ""
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
document.warehouse.name
|
||||||
|
if document.warehouse is not None
|
||||||
|
else ""
|
||||||
|
),
|
||||||
(
|
(
|
||||||
document.owner.username
|
document.owner.username
|
||||||
if document.owner is not None
|
if document.owner is not None
|
||||||
@ -693,6 +755,16 @@ def run_workflow(
|
|||||||
):
|
):
|
||||||
document.correspondent = None
|
document.correspondent = None
|
||||||
|
|
||||||
|
if action.remove_all_warehouses or (
|
||||||
|
document.warehouse
|
||||||
|
and (
|
||||||
|
action.remove_warehouses.filter(
|
||||||
|
pk=document.warehouse.pk,
|
||||||
|
).exists()
|
||||||
|
)
|
||||||
|
):
|
||||||
|
document.warehouse = None
|
||||||
|
|
||||||
if action.remove_all_document_types or (
|
if action.remove_all_document_types or (
|
||||||
document.document_type
|
document.document_type
|
||||||
and (
|
and (
|
||||||
|
@ -33,6 +33,7 @@ from documents.models import Correspondent
|
|||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
|
from documents.models import Warehouse
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.parsers import DocumentParser
|
from documents.parsers import DocumentParser
|
||||||
from documents.parsers import get_parser_class_for_mime_type
|
from documents.parsers import get_parser_class_for_mime_type
|
||||||
@ -73,6 +74,7 @@ def train_classifier():
|
|||||||
not Tag.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
not Tag.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
||||||
and not DocumentType.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
and not DocumentType.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
||||||
and not Correspondent.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
and not Correspondent.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
||||||
|
and not Warehouse.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
||||||
and not StoragePath.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
and not StoragePath.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
|
||||||
):
|
):
|
||||||
logger.info("No automatic matching items, not training")
|
logger.info("No automatic matching items, not training")
|
||||||
@ -170,6 +172,7 @@ def consume_file(
|
|||||||
override_correspondent_id=overrides.correspondent_id,
|
override_correspondent_id=overrides.correspondent_id,
|
||||||
override_document_type_id=overrides.document_type_id,
|
override_document_type_id=overrides.document_type_id,
|
||||||
override_tag_ids=overrides.tag_ids,
|
override_tag_ids=overrides.tag_ids,
|
||||||
|
override_warehouse_id=overrides.warehouse_id,
|
||||||
override_storage_path_id=overrides.storage_path_id,
|
override_storage_path_id=overrides.storage_path_id,
|
||||||
override_created=overrides.created,
|
override_created=overrides.created,
|
||||||
override_asn=overrides.asn,
|
override_asn=overrides.asn,
|
||||||
|
@ -104,6 +104,7 @@ from documents.filters import WarehouseFilterSet
|
|||||||
from documents.matching import match_correspondents
|
from documents.matching import match_correspondents
|
||||||
from documents.matching import match_document_types
|
from documents.matching import match_document_types
|
||||||
from documents.matching import match_storage_paths
|
from documents.matching import match_storage_paths
|
||||||
|
from documents.matching import match_warehouses
|
||||||
from documents.matching import match_tags
|
from documents.matching import match_tags
|
||||||
from documents.models import Correspondent
|
from documents.models import Correspondent
|
||||||
from documents.models import CustomField
|
from documents.models import CustomField
|
||||||
@ -336,7 +337,7 @@ class DocumentViewSet(
|
|||||||
ObjectOwnedOrGrantedPermissionsFilter,
|
ObjectOwnedOrGrantedPermissionsFilter,
|
||||||
)
|
)
|
||||||
filterset_class = DocumentFilterSet
|
filterset_class = DocumentFilterSet
|
||||||
search_fields = ("title", "correspondent__name", "content", "warehouses")
|
search_fields = ("title", "correspondent__name", "content", "warehouse")
|
||||||
ordering_fields = (
|
ordering_fields = (
|
||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
@ -354,7 +355,7 @@ class DocumentViewSet(
|
|||||||
return (
|
return (
|
||||||
Document.objects.distinct()
|
Document.objects.distinct()
|
||||||
.annotate(num_notes=Count("notes"))
|
.annotate(num_notes=Count("notes"))
|
||||||
.select_related("correspondent", "storage_path", "document_type", "owner")
|
.select_related("correspondent", "storage_path", "document_type","warehouse", "owner")
|
||||||
.prefetch_related("tags", "custom_fields", "notes")
|
.prefetch_related("tags", "custom_fields", "notes")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -529,6 +530,9 @@ class DocumentViewSet(
|
|||||||
"correspondents": [
|
"correspondents": [
|
||||||
c.id for c in match_correspondents(doc, classifier, request.user)
|
c.id for c in match_correspondents(doc, classifier, request.user)
|
||||||
],
|
],
|
||||||
|
"warehouses": [
|
||||||
|
wh.id for wh in match_warehouses(doc, classifier, request.user)
|
||||||
|
],
|
||||||
"tags": [t.id for t in match_tags(doc, classifier, request.user)],
|
"tags": [t.id for t in match_tags(doc, classifier, request.user)],
|
||||||
"document_types": [
|
"document_types": [
|
||||||
dt.id for dt in match_document_types(doc, classifier, request.user)
|
dt.id for dt in match_document_types(doc, classifier, request.user)
|
||||||
@ -749,6 +753,7 @@ class SearchResultSerializer(DocumentSerializer, PassUserMixin):
|
|||||||
"correspondent",
|
"correspondent",
|
||||||
"storage_path",
|
"storage_path",
|
||||||
"document_type",
|
"document_type",
|
||||||
|
"warehouse"
|
||||||
"owner",
|
"owner",
|
||||||
)
|
)
|
||||||
.prefetch_related("tags", "custom_fields", "notes")
|
.prefetch_related("tags", "custom_fields", "notes")
|
||||||
@ -937,6 +942,7 @@ class PostDocumentView(GenericAPIView):
|
|||||||
correspondent_id = serializer.validated_data.get("correspondent")
|
correspondent_id = serializer.validated_data.get("correspondent")
|
||||||
document_type_id = serializer.validated_data.get("document_type")
|
document_type_id = serializer.validated_data.get("document_type")
|
||||||
storage_path_id = serializer.validated_data.get("storage_path")
|
storage_path_id = serializer.validated_data.get("storage_path")
|
||||||
|
warehouse_id = serializer.validated_data.get("warehouse")
|
||||||
tag_ids = serializer.validated_data.get("tags")
|
tag_ids = serializer.validated_data.get("tags")
|
||||||
title = serializer.validated_data.get("title")
|
title = serializer.validated_data.get("title")
|
||||||
created = serializer.validated_data.get("created")
|
created = serializer.validated_data.get("created")
|
||||||
@ -965,6 +971,7 @@ class PostDocumentView(GenericAPIView):
|
|||||||
correspondent_id=correspondent_id,
|
correspondent_id=correspondent_id,
|
||||||
document_type_id=document_type_id,
|
document_type_id=document_type_id,
|
||||||
storage_path_id=storage_path_id,
|
storage_path_id=storage_path_id,
|
||||||
|
warehouse_id=warehouse_id,
|
||||||
tag_ids=tag_ids,
|
tag_ids=tag_ids,
|
||||||
created=created,
|
created=created,
|
||||||
asn=archive_serial_number,
|
asn=archive_serial_number,
|
||||||
@ -1015,6 +1022,12 @@ class SelectionDataView(GenericAPIView):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
warehouses = Warehouse.objects.annotate(
|
||||||
|
document_count=Count(
|
||||||
|
Case(When(documents__id__in=ids, then=1), output_field=IntegerField()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
r = Response(
|
r = Response(
|
||||||
{
|
{
|
||||||
"selected_correspondents": [
|
"selected_correspondents": [
|
||||||
@ -1027,6 +1040,9 @@ class SelectionDataView(GenericAPIView):
|
|||||||
"selected_document_types": [
|
"selected_document_types": [
|
||||||
{"id": t.id, "document_count": t.document_count} for t in types
|
{"id": t.id, "document_count": t.document_count} for t in types
|
||||||
],
|
],
|
||||||
|
"selected_warehouses": [
|
||||||
|
{"id": t.id, "document_count": t.document_count} for t in warehouses
|
||||||
|
],
|
||||||
"selected_storage_paths": [
|
"selected_storage_paths": [
|
||||||
{"id": t.id, "document_count": t.document_count}
|
{"id": t.id, "document_count": t.document_count}
|
||||||
for t in storage_paths
|
for t in storage_paths
|
||||||
@ -1111,6 +1127,17 @@ class StatisticsView(APIView):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
warehouse_count = (
|
||||||
|
Warehouse.objects.count()
|
||||||
|
if user is None
|
||||||
|
else len(
|
||||||
|
get_objects_for_user_owner_aware(
|
||||||
|
user,
|
||||||
|
"documents.view_warehouse",
|
||||||
|
Warehouse,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
storage_path_count = (
|
storage_path_count = (
|
||||||
StoragePath.objects.count()
|
StoragePath.objects.count()
|
||||||
if user is None
|
if user is None
|
||||||
@ -1160,6 +1187,7 @@ class StatisticsView(APIView):
|
|||||||
"correspondent_count": correspondent_count,
|
"correspondent_count": correspondent_count,
|
||||||
"document_type_count": document_type_count,
|
"document_type_count": document_type_count,
|
||||||
"storage_path_count": storage_path_count,
|
"storage_path_count": storage_path_count,
|
||||||
|
"warehouse_count": warehouse_count,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1531,18 +1559,18 @@ class BulkEditObjectsView(PassUserMixin):
|
|||||||
|
|
||||||
if warehouse.type == Warehouse.SHELF:
|
if warehouse.type == Warehouse.SHELF:
|
||||||
boxcases = Warehouse.objects.filter(parent_warehouse=warehouse)
|
boxcases = Warehouse.objects.filter(parent_warehouse=warehouse)
|
||||||
documents = Document.objects.filter(warehouses__in=[b.id for b in boxcases])
|
documents = Document.objects.filter(warehouse__in=[b.id for b in boxcases])
|
||||||
documents.delete()
|
documents.delete()
|
||||||
boxcases.delete()
|
boxcases.delete()
|
||||||
warehouse.delete()
|
warehouse.delete()
|
||||||
if warehouse.type == Warehouse.BOXCASE:
|
if warehouse.type == Warehouse.BOXCASE:
|
||||||
documents = Document.objects.filter(warehouses=warehouse)
|
documents = Document.objects.filter(warehouse=warehouse)
|
||||||
documents.delete()
|
documents.delete()
|
||||||
warehouse.delete()
|
warehouse.delete()
|
||||||
if warehouse.type == Warehouse.WAREHOUSE:
|
if warehouse.type == Warehouse.WAREHOUSE:
|
||||||
shelves = Warehouse.objects.filter(parent_warehouse=warehouse)
|
shelves = Warehouse.objects.filter(parent_warehouse=warehouse)
|
||||||
boxcases = Warehouse.objects.filter(parent_warehouse__in=[s.id for s in shelves])
|
boxcases = Warehouse.objects.filter(parent_warehouse__in=[s.id for s in shelves])
|
||||||
documents = Document.objects.filter(warehouses__in=[b.id for b in boxcases])
|
documents = Document.objects.filter(warehouse__in=[b.id for b in boxcases])
|
||||||
documents.delete()
|
documents.delete()
|
||||||
boxcases.delete()
|
boxcases.delete()
|
||||||
shelves.delete()
|
shelves.delete()
|
||||||
@ -1718,6 +1746,9 @@ class SystemStatusView(PassUserMixin):
|
|||||||
or Correspondent.objects.filter(
|
or Correspondent.objects.filter(
|
||||||
matching_algorithm=Tag.MATCH_AUTO,
|
matching_algorithm=Tag.MATCH_AUTO,
|
||||||
).exists()
|
).exists()
|
||||||
|
or Warehouse.objects.filter(
|
||||||
|
matching_algorithm=Tag.MATCH_AUTO,
|
||||||
|
).exists()
|
||||||
or StoragePath.objects.filter(
|
or StoragePath.objects.filter(
|
||||||
matching_algorithm=Tag.MATCH_AUTO,
|
matching_algorithm=Tag.MATCH_AUTO,
|
||||||
).exists()
|
).exists()
|
||||||
@ -1812,31 +1843,10 @@ class WarehouseViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
|
|||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
# try:
|
# try:
|
||||||
serializer = WarehouseSerializer(data=request.data)
|
serializer = WarehouseSerializer(data=request.data)
|
||||||
name = None
|
|
||||||
type = None
|
|
||||||
parent_warehouse = None
|
parent_warehouse = None
|
||||||
if serializer.is_valid(raise_exception=True):
|
if serializer.is_valid(raise_exception=True):
|
||||||
name = serializer.validated_data.get("name", "")
|
|
||||||
type = serializer.validated_data.get("type", Warehouse.WAREHOUSE)
|
|
||||||
parent_warehouse = serializer.validated_data.get('parent_warehouse',None)
|
parent_warehouse = serializer.validated_data.get('parent_warehouse',None)
|
||||||
# check_warehouse = Warehouse.objects.filter(
|
|
||||||
# name = name,
|
|
||||||
# type = type,
|
|
||||||
# parent_warehouse=parent_warehouse
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if check_warehouse:
|
|
||||||
# return Response({'status':400,
|
|
||||||
# 'message':'created fail'},status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# if type == Warehouse.SHELF and parent_warehouse == None:
|
|
||||||
# return Response({'status': 400,
|
|
||||||
# 'message': 'parent_warehouse is required for Shelf type'}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
# elif type == Warehouse.BOXCASE and parent_warehouse == None:
|
|
||||||
# return Response({'status': 400,
|
|
||||||
# 'message': 'parent_warehouse is required for Boxcase type'}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# if serializer.is_valid(raise_exception=True):
|
|
||||||
parent_warehouse = Warehouse.objects.filter(id=parent_warehouse.id if parent_warehouse else 0).first()
|
parent_warehouse = Warehouse.objects.filter(id=parent_warehouse.id if parent_warehouse else 0).first()
|
||||||
|
|
||||||
if serializer.validated_data.get("type") == Warehouse.WAREHOUSE and not parent_warehouse:
|
if serializer.validated_data.get("type") == Warehouse.WAREHOUSE and not parent_warehouse:
|
||||||
@ -1853,7 +1863,27 @@ class WarehouseViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
|
|||||||
'message':'created successfully',
|
'message':'created successfully',
|
||||||
'data':serializer.data},status=status.HTTP_201_CREATED)
|
'data':serializer.data},status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
# except Exception as e:
|
|
||||||
# return Response({'status':400,
|
def destroy(self, request, pk, *args, **kwargs):
|
||||||
# 'message':e},status=status.HTTP_400_BAD_REQUEST)
|
warehouse = Warehouse.objects.get(id=pk)
|
||||||
|
if warehouse.type == Warehouse.SHELF:
|
||||||
|
boxcases = Warehouse.objects.filter(parent_warehouse=warehouse)
|
||||||
|
documents = Document.objects.filter(warehouse__in=[b.id for b in boxcases])
|
||||||
|
documents.delete()
|
||||||
|
boxcases.delete()
|
||||||
|
warehouse.delete()
|
||||||
|
if warehouse.type == Warehouse.BOXCASE:
|
||||||
|
documents = Document.objects.filter(warehouse=warehouse)
|
||||||
|
documents.delete()
|
||||||
|
warehouse.delete()
|
||||||
|
if warehouse.type == Warehouse.WAREHOUSE:
|
||||||
|
shelves = Warehouse.objects.filter(parent_warehouse=warehouse)
|
||||||
|
boxcases = Warehouse.objects.filter(parent_warehouse__in=[s.id for s in shelves])
|
||||||
|
documents = Document.objects.filter(warehouse__in=[b.id for b in boxcases])
|
||||||
|
documents.delete()
|
||||||
|
boxcases.delete()
|
||||||
|
shelves.delete()
|
||||||
|
warehouse.delete()
|
||||||
|
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user