mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Merge pull request #2893 from paperless-ngx/feature-enhanced-object-filtering
Enhancement: support filtering multiple correspondents, doctypes & storage paths
This commit is contained in:
		@@ -48,6 +48,26 @@ describe('documents-list', () => {
 | 
				
			|||||||
            (d.tags as Array<number>).includes(tag_id)
 | 
					            (d.tags as Array<number>).includes(tag_id)
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
          response.count = response.results.length
 | 
					          response.count = response.results.length
 | 
				
			||||||
 | 
					        } else if (req.query.hasOwnProperty('correspondent__id__in')) {
 | 
				
			||||||
 | 
					          // filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&correspondent__id__in=9,14
 | 
				
			||||||
 | 
					          const correspondent_ids = req.query['correspondent__id__in']
 | 
				
			||||||
 | 
					            .toString()
 | 
				
			||||||
 | 
					            .split(',')
 | 
				
			||||||
 | 
					            .map((c) => +c)
 | 
				
			||||||
 | 
					          response.results = (documentsJson.results as Array<any>).filter((d) =>
 | 
				
			||||||
 | 
					            correspondent_ids.includes(d.correspondent)
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          response.count = response.results.length
 | 
				
			||||||
 | 
					        } else if (req.query.hasOwnProperty('correspondent__id__none')) {
 | 
				
			||||||
 | 
					          // filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&correspondent__id__none=9,14
 | 
				
			||||||
 | 
					          const correspondent_ids = req.query['correspondent__id__none']
 | 
				
			||||||
 | 
					            .toString()
 | 
				
			||||||
 | 
					            .split(',')
 | 
				
			||||||
 | 
					            .map((c) => +c)
 | 
				
			||||||
 | 
					          response.results = (documentsJson.results as Array<any>).filter(
 | 
				
			||||||
 | 
					            (d) => !correspondent_ids.includes(d.correspondent)
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          response.count = response.results.length
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        req.reply(response)
 | 
					        req.reply(response)
 | 
				
			||||||
@@ -112,6 +132,27 @@ describe('documents-list', () => {
 | 
				
			|||||||
    cy.contains('One document')
 | 
					    cy.contains('One document')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should filter including multiple correspondents', () => {
 | 
				
			||||||
 | 
					    cy.get('app-filter-editor app-filterable-dropdown[title="Correspondent"]')
 | 
				
			||||||
 | 
					      .click()
 | 
				
			||||||
 | 
					      .within(() => {
 | 
				
			||||||
 | 
					        cy.contains('button', 'ABC Test Correspondent').click()
 | 
				
			||||||
 | 
					        cy.contains('button', 'Corresp 11').click()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    cy.contains('3 documents')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should filter excluding multiple correspondents', () => {
 | 
				
			||||||
 | 
					    cy.get('app-filter-editor app-filterable-dropdown[title="Correspondent"]')
 | 
				
			||||||
 | 
					      .click()
 | 
				
			||||||
 | 
					      .within(() => {
 | 
				
			||||||
 | 
					        cy.contains('button', 'ABC Test Correspondent').click()
 | 
				
			||||||
 | 
					        cy.contains('button', 'Corresp 11').click()
 | 
				
			||||||
 | 
					        cy.contains('label', 'Exclude').click()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    cy.contains('One document')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should apply tags', () => {
 | 
					  it('should apply tags', () => {
 | 
				
			||||||
    cy.get('app-document-card-small:first-of-type').click()
 | 
					    cy.get('app-document-card-small:first-of-type').click()
 | 
				
			||||||
    cy.get('app-bulk-editor app-filterable-dropdown[title="Tags"]').within(
 | 
					    cy.get('app-bulk-editor app-filterable-dropdown[title="Tags"]').within(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -232,6 +232,11 @@ describe('documents query params', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  it('should show a list of documents filtered by document type', () => {
 | 
					  it('should show a list of documents filtered by document type', () => {
 | 
				
			||||||
    cy.visit('/documents?sort=created&reverse=true&document_type__id=1')
 | 
					    cy.visit('/documents?sort=created&reverse=true&document_type__id=1')
 | 
				
			||||||
 | 
					    cy.contains('2 documents')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should show a list of documents filtered by multiple correspondents', () => {
 | 
				
			||||||
 | 
					    cy.visit('/documents?sort=created&reverse=true&document_type__id__in=1,2')
 | 
				
			||||||
    cy.contains('3 documents')
 | 
					    cy.contains('3 documents')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -245,9 +250,14 @@ describe('documents query params', () => {
 | 
				
			|||||||
    cy.contains('2 documents')
 | 
					    cy.contains('2 documents')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should show a list of documents filtered by multiple correspondents', () => {
 | 
				
			||||||
 | 
					    cy.visit('/documents?sort=created&reverse=true&correspondent__id__in=9,14')
 | 
				
			||||||
 | 
					    cy.contains('3 documents')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should show a list of documents filtered by no correspondent', () => {
 | 
					  it('should show a list of documents filtered by no correspondent', () => {
 | 
				
			||||||
    cy.visit('/documents?sort=created&reverse=true&correspondent__isnull=1')
 | 
					    cy.visit('/documents?sort=created&reverse=true&correspondent__isnull=1')
 | 
				
			||||||
    cy.contains('2 documents')
 | 
					    cy.contains('One document')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should show a list of documents filtered by storage path', () => {
 | 
					  it('should show a list of documents filtered by storage path', () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,257 @@
 | 
				
			|||||||
{"count":27,"next":"http://localhost:8000/api/correspondents/?page=2","previous":null,"results":[{"id":9,"slug":"abc-test-correspondent","name":"ABC Test Correspondent","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":13,"slug":"corresp-10","name":"Corresp 10","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":14,"slug":"corresp-11","name":"Corresp 11","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":15,"slug":"corresp-12","name":"Corresp 12","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":16,"slug":"corresp-13","name":"Corresp 13","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":18,"slug":"corresp-15","name":"Corresp 15","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":19,"slug":"corresp-16","name":"Corresp 16","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":20,"slug":"corresp-17","name":"Corresp 17","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":21,"slug":"corresp-18","name":"Corresp 18","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":22,"slug":"corresp-19","name":"Corresp 19","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":23,"slug":"corresp-20","name":"Corresp 20","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":24,"slug":"corresp-21","name":"Corresp 21","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":25,"slug":"corresp-22","name":"Corresp 22","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":26,"slug":"corresp-23","name":"Corresp 23","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":5,"slug":"corresp-3","name":"Corresp 3","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":6,"slug":"corresp-4","name":"Corresp 4","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":7,"slug":"corresp-5","name":"Corresp 5","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":8,"slug":"corresp-6","name":"Corresp 6","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":10,"slug":"corresp-7","name":"Corresp 7","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":11,"slug":"corresp-8","name":"Corresp 8","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":12,"slug":"corresp-9","name":"Corresp 9","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":17,"slug":"correspondent-14","name":"Correspondent 14","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":2,"slug":"correspondent-2","name":"Correspondent 2","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":7,"last_correspondence":"2021-01-20T23:37:58.204614Z"},{"id":27,"slug":"michael-shamoon","name":"Michael Shamoon","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":1,"last_correspondence":"2022-03-16T03:48:50.089624Z"},{"id":4,"slug":"newest-correspondent","name":"Newest Correspondent","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":1,"last_correspondence":"2021-02-07T08:00:00Z"}]}
 | 
					{
 | 
				
			||||||
 | 
					    "count": 27,
 | 
				
			||||||
 | 
					    "next": "http://localhost:8000/api/correspondents/?page=2",
 | 
				
			||||||
 | 
					    "previous": null,
 | 
				
			||||||
 | 
					    "results": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 9,
 | 
				
			||||||
 | 
					            "slug": "abc-test-correspondent",
 | 
				
			||||||
 | 
					            "name": "ABC Test Correspondent",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 13,
 | 
				
			||||||
 | 
					            "slug": "corresp-10",
 | 
				
			||||||
 | 
					            "name": "Corresp 10",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 14,
 | 
				
			||||||
 | 
					            "slug": "corresp-11",
 | 
				
			||||||
 | 
					            "name": "Corresp 11",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 15,
 | 
				
			||||||
 | 
					            "slug": "corresp-12",
 | 
				
			||||||
 | 
					            "name": "Corresp 12",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 16,
 | 
				
			||||||
 | 
					            "slug": "corresp-13",
 | 
				
			||||||
 | 
					            "name": "Corresp 13",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 18,
 | 
				
			||||||
 | 
					            "slug": "corresp-15",
 | 
				
			||||||
 | 
					            "name": "Corresp 15",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 19,
 | 
				
			||||||
 | 
					            "slug": "corresp-16",
 | 
				
			||||||
 | 
					            "name": "Corresp 16",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 20,
 | 
				
			||||||
 | 
					            "slug": "corresp-17",
 | 
				
			||||||
 | 
					            "name": "Corresp 17",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 21,
 | 
				
			||||||
 | 
					            "slug": "corresp-18",
 | 
				
			||||||
 | 
					            "name": "Corresp 18",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 22,
 | 
				
			||||||
 | 
					            "slug": "corresp-19",
 | 
				
			||||||
 | 
					            "name": "Corresp 19",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 23,
 | 
				
			||||||
 | 
					            "slug": "corresp-20",
 | 
				
			||||||
 | 
					            "name": "Corresp 20",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 24,
 | 
				
			||||||
 | 
					            "slug": "corresp-21",
 | 
				
			||||||
 | 
					            "name": "Corresp 21",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 25,
 | 
				
			||||||
 | 
					            "slug": "corresp-22",
 | 
				
			||||||
 | 
					            "name": "Corresp 22",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 26,
 | 
				
			||||||
 | 
					            "slug": "corresp-23",
 | 
				
			||||||
 | 
					            "name": "Corresp 23",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 5,
 | 
				
			||||||
 | 
					            "slug": "corresp-3",
 | 
				
			||||||
 | 
					            "name": "Corresp 3",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 6,
 | 
				
			||||||
 | 
					            "slug": "corresp-4",
 | 
				
			||||||
 | 
					            "name": "Corresp 4",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 7,
 | 
				
			||||||
 | 
					            "slug": "corresp-5",
 | 
				
			||||||
 | 
					            "name": "Corresp 5",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 8,
 | 
				
			||||||
 | 
					            "slug": "corresp-6",
 | 
				
			||||||
 | 
					            "name": "Corresp 6",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 10,
 | 
				
			||||||
 | 
					            "slug": "corresp-7",
 | 
				
			||||||
 | 
					            "name": "Corresp 7",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 11,
 | 
				
			||||||
 | 
					            "slug": "corresp-8",
 | 
				
			||||||
 | 
					            "name": "Corresp 8",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 12,
 | 
				
			||||||
 | 
					            "slug": "corresp-9",
 | 
				
			||||||
 | 
					            "name": "Corresp 9",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 17,
 | 
				
			||||||
 | 
					            "slug": "correspondent-14",
 | 
				
			||||||
 | 
					            "name": "Correspondent 14",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 0,
 | 
				
			||||||
 | 
					            "last_correspondence": null
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 2,
 | 
				
			||||||
 | 
					            "slug": "correspondent-2",
 | 
				
			||||||
 | 
					            "name": "Correspondent 2",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 7,
 | 
				
			||||||
 | 
					            "last_correspondence": "2021-01-20T23:37:58.204614Z"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 27,
 | 
				
			||||||
 | 
					            "slug": "correspondent-slug",
 | 
				
			||||||
 | 
					            "name": "Correspondent Slug",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 1,
 | 
				
			||||||
 | 
					            "last_correspondence": "2022-03-16T03:48:50.089624Z"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 4,
 | 
				
			||||||
 | 
					            "slug": "newest-correspondent",
 | 
				
			||||||
 | 
					            "name": "Newest Correspondent",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 1,
 | 
				
			||||||
 | 
					            "last_correspondence": "2021-02-07T08:00:00Z"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,25 @@
 | 
				
			|||||||
{"count":1,"next":null,"previous":null,"results":[{"id":1,"slug":"test","name":"Test Doc Type","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0}]}
 | 
					{
 | 
				
			||||||
 | 
					    "count": 2,
 | 
				
			||||||
 | 
					    "next": null,
 | 
				
			||||||
 | 
					    "previous": null,
 | 
				
			||||||
 | 
					    "results": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 1,
 | 
				
			||||||
 | 
					            "slug": "test",
 | 
				
			||||||
 | 
					            "name": "Test Doc Type",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 1
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "id": 2,
 | 
				
			||||||
 | 
					            "slug": "test2",
 | 
				
			||||||
 | 
					            "name": "Test Doc Type 2",
 | 
				
			||||||
 | 
					            "match": "",
 | 
				
			||||||
 | 
					            "matching_algorithm": 1,
 | 
				
			||||||
 | 
					            "is_insensitive": true,
 | 
				
			||||||
 | 
					            "document_count": 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "id": 3,
 | 
					            "id": 3,
 | 
				
			||||||
            "correspondent": null,
 | 
					            "correspondent": 14,
 | 
				
			||||||
            "document_type": 1,
 | 
					            "document_type": 1,
 | 
				
			||||||
            "storage_path": null,
 | 
					            "storage_path": null,
 | 
				
			||||||
            "title": "dolor",
 | 
					            "title": "dolor",
 | 
				
			||||||
@@ -64,7 +64,7 @@
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            "id": 4,
 | 
					            "id": 4,
 | 
				
			||||||
            "correspondent": 9,
 | 
					            "correspondent": 9,
 | 
				
			||||||
            "document_type": 1,
 | 
					            "document_type": 2,
 | 
				
			||||||
            "storage_path": null,
 | 
					            "storage_path": null,
 | 
				
			||||||
            "title": "sit amet",
 | 
					            "title": "sit amet",
 | 
				
			||||||
            "content": "Test document PDF",
 | 
					            "content": "Test document PDF",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1794,25 +1794,39 @@
 | 
				
			|||||||
          <context context-type="linenumber">18</context>
 | 
					          <context context-type="linenumber">18</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
 | 
					      <trans-unit id="6381578200008167206" datatype="html">
 | 
				
			||||||
 | 
					        <source>Include</source>
 | 
				
			||||||
 | 
					        <context-group purpose="location">
 | 
				
			||||||
 | 
					          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
				
			||||||
 | 
					          <context context-type="linenumber">24</context>
 | 
				
			||||||
 | 
					        </context-group>
 | 
				
			||||||
 | 
					      </trans-unit>
 | 
				
			||||||
 | 
					      <trans-unit id="5668077948386857930" datatype="html">
 | 
				
			||||||
 | 
					        <source>Exclude</source>
 | 
				
			||||||
 | 
					        <context-group purpose="location">
 | 
				
			||||||
 | 
					          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
				
			||||||
 | 
					          <context context-type="linenumber">26</context>
 | 
				
			||||||
 | 
					        </context-group>
 | 
				
			||||||
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="4391289919356861627" datatype="html">
 | 
					      <trans-unit id="4391289919356861627" datatype="html">
 | 
				
			||||||
        <source>Apply</source>
 | 
					        <source>Apply</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
					          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
				
			||||||
          <context context-type="linenumber">32</context>
 | 
					          <context context-type="linenumber">40</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="7780041345210191160" datatype="html">
 | 
					      <trans-unit id="7780041345210191160" datatype="html">
 | 
				
			||||||
        <source>Click again to exclude items.</source>
 | 
					        <source>Click again to exclude items.</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
					          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
 | 
				
			||||||
          <context context-type="linenumber">38</context>
 | 
					          <context context-type="linenumber">46</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="7593728289020204896" datatype="html">
 | 
					      <trans-unit id="7593728289020204896" datatype="html">
 | 
				
			||||||
        <source>Not assigned</source>
 | 
					        <source>Not assigned</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">262</context>
 | 
					          <context context-type="linenumber">321</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <note priority="1" from="description">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
 | 
					        <note priority="1" from="description">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
@@ -2174,7 +2188,7 @@
 | 
				
			|||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">162</context>
 | 
					          <context context-type="linenumber">172</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
 | 
					          <context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
 | 
				
			||||||
@@ -3331,7 +3345,7 @@
 | 
				
			|||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">167</context>
 | 
					          <context context-type="linenumber">177</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
 | 
					          <context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
 | 
				
			||||||
@@ -3385,112 +3399,112 @@
 | 
				
			|||||||
        <source>Correspondent: <x id="PH" equiv-text="this.correspondents.find((c) => c.id == +rule.value)?.name"/></source>
 | 
					        <source>Correspondent: <x id="PH" equiv-text="this.correspondents.find((c) => c.id == +rule.value)?.name"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">98,100</context>
 | 
					          <context context-type="linenumber">108,110</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8170755470576301659" datatype="html">
 | 
					      <trans-unit id="8170755470576301659" datatype="html">
 | 
				
			||||||
        <source>Without correspondent</source>
 | 
					        <source>Without correspondent</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">102</context>
 | 
					          <context context-type="linenumber">112</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8705701325879965907" datatype="html">
 | 
					      <trans-unit id="8705701325879965907" datatype="html">
 | 
				
			||||||
        <source>Type: <x id="PH" equiv-text="this.documentTypes.find((dt) => dt.id == +rule.value)?.name"/></source>
 | 
					        <source>Type: <x id="PH" equiv-text="this.documentTypes.find((dt) => dt.id == +rule.value)?.name"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">107,109</context>
 | 
					          <context context-type="linenumber">117,119</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="4362173610367509215" datatype="html">
 | 
					      <trans-unit id="4362173610367509215" datatype="html">
 | 
				
			||||||
        <source>Without document type</source>
 | 
					        <source>Without document type</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">111</context>
 | 
					          <context context-type="linenumber">121</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8180755793012580465" datatype="html">
 | 
					      <trans-unit id="8180755793012580465" datatype="html">
 | 
				
			||||||
        <source>Tag: <x id="PH" equiv-text="this.tags.find((t) => t.id == +rule.value)?.name"/></source>
 | 
					        <source>Tag: <x id="PH" equiv-text="this.tags.find((t) => t.id == +rule.value)?.name"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">115,117</context>
 | 
					          <context context-type="linenumber">125,127</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="6494566478302448576" datatype="html">
 | 
					      <trans-unit id="6494566478302448576" datatype="html">
 | 
				
			||||||
        <source>Without any tag</source>
 | 
					        <source>Without any tag</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">121</context>
 | 
					          <context context-type="linenumber">131</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="6523384805359286307" datatype="html">
 | 
					      <trans-unit id="6523384805359286307" datatype="html">
 | 
				
			||||||
        <source>Title: <x id="PH" equiv-text="rule.value"/></source>
 | 
					        <source>Title: <x id="PH" equiv-text="rule.value"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">125</context>
 | 
					          <context context-type="linenumber">135</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="1872523635812236432" datatype="html">
 | 
					      <trans-unit id="1872523635812236432" datatype="html">
 | 
				
			||||||
        <source>ASN: <x id="PH" equiv-text="rule.value"/></source>
 | 
					        <source>ASN: <x id="PH" equiv-text="rule.value"/></source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">128</context>
 | 
					          <context context-type="linenumber">138</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="3100631071441658964" datatype="html">
 | 
					      <trans-unit id="3100631071441658964" datatype="html">
 | 
				
			||||||
        <source>Title & content</source>
 | 
					        <source>Title & content</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">165</context>
 | 
					          <context context-type="linenumber">175</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="1010505078885609376" datatype="html">
 | 
					      <trans-unit id="1010505078885609376" datatype="html">
 | 
				
			||||||
        <source>Advanced search</source>
 | 
					        <source>Advanced search</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">170</context>
 | 
					          <context context-type="linenumber">180</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="2649431021108393503" datatype="html">
 | 
					      <trans-unit id="2649431021108393503" datatype="html">
 | 
				
			||||||
        <source>More like</source>
 | 
					        <source>More like</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">176</context>
 | 
					          <context context-type="linenumber">186</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="3697582909018473071" datatype="html">
 | 
					      <trans-unit id="3697582909018473071" datatype="html">
 | 
				
			||||||
        <source>equals</source>
 | 
					        <source>equals</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">195</context>
 | 
					          <context context-type="linenumber">205</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="5325481293405718739" datatype="html">
 | 
					      <trans-unit id="5325481293405718739" datatype="html">
 | 
				
			||||||
        <source>is empty</source>
 | 
					        <source>is empty</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">199</context>
 | 
					          <context context-type="linenumber">209</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="6166785695326182482" datatype="html">
 | 
					      <trans-unit id="6166785695326182482" datatype="html">
 | 
				
			||||||
        <source>is not empty</source>
 | 
					        <source>is not empty</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">203</context>
 | 
					          <context context-type="linenumber">213</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="4686622206659266699" datatype="html">
 | 
					      <trans-unit id="4686622206659266699" datatype="html">
 | 
				
			||||||
        <source>greater than</source>
 | 
					        <source>greater than</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">207</context>
 | 
					          <context context-type="linenumber">217</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="8014012170270529279" datatype="html">
 | 
					      <trans-unit id="8014012170270529279" datatype="html">
 | 
				
			||||||
        <source>less than</source>
 | 
					        <source>less than</source>
 | 
				
			||||||
        <context-group purpose="location">
 | 
					        <context-group purpose="location">
 | 
				
			||||||
          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
					          <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
 | 
				
			||||||
          <context context-type="linenumber">211</context>
 | 
					          <context context-type="linenumber">221</context>
 | 
				
			||||||
        </context-group>
 | 
					        </context-group>
 | 
				
			||||||
      </trans-unit>
 | 
					      </trans-unit>
 | 
				
			||||||
      <trans-unit id="7210076240260527720" datatype="html">
 | 
					      <trans-unit id="7210076240260527720" datatype="html">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,29 @@
 | 
				
			|||||||
<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
 | 
					<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
 | 
				
			||||||
  <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
 | 
					  <button class="btn btn-sm" id="dropdown_{{name}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
 | 
				
			||||||
    <svg class="toolbaricon" fill="currentColor">
 | 
					    <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
      <use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
 | 
					      <use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
 | 
				
			||||||
    </svg>
 | 
					    </svg>
 | 
				
			||||||
    <div class="d-none d-sm-inline"> {{title}}</div>
 | 
					    <div class="d-none d-sm-inline"> {{title}}</div>
 | 
				
			||||||
    <ng-container *ngIf="!editing && selectionModel.totalCount > 0">
 | 
					    <ng-container *ngIf="!editing && selectionModel.totalCount > 0">
 | 
				
			||||||
      <app-clearable-badge [number]="multiple ? selectionModel.totalCount : undefined" [selected]="!multiple && selectionModel.selectionSize() > 0" (cleared)="reset()"></app-clearable-badge>
 | 
					      <app-clearable-badge [number]="selectionModel.totalCount" [selected]="selectionModel.selectionSize() > 0" (cleared)="reset()"></app-clearable-badge>
 | 
				
			||||||
    </ng-container>
 | 
					    </ng-container>
 | 
				
			||||||
  </button>
 | 
					  </button>
 | 
				
			||||||
  <div class="dropdown-menu py-0 shadow" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">
 | 
					  <div class="dropdown-menu py-0 shadow" ngbDropdownMenu attr.aria-labelledby="dropdown_{{name}}">
 | 
				
			||||||
    <div class="list-group list-group-flush">
 | 
					    <div class="list-group list-group-flush">
 | 
				
			||||||
      <div *ngIf="!editing && multiple" class="list-group-item d-flex">
 | 
					      <div *ngIf="!editing && manyToOne" class="list-group-item d-flex">
 | 
				
			||||||
        <div class="btn-group btn-group-xs flex-fill">
 | 
					        <div class="btn-group btn-group-xs flex-fill" role="group">
 | 
				
			||||||
          <input [(ngModel)]="selectionModel.logicalOperator" [disabled]="!operatorToggleEnabled" (ngModelChange)="selectionModel.toggleOperator()" type="radio" class="btn-check" id="logicalOperatorAnd" value="and">
 | 
					          <input [(ngModel)]="selectionModel.logicalOperator" [disabled]="!modifierToggleEnabled" (ngModelChange)="selectionModel.toggleOperator()" type="radio" class="btn-check" id="logicalOperatorAnd_{{name}}" name="logicalOperatorAnd_{{name}}" value="and">
 | 
				
			||||||
          <label class="btn btn-outline-primary" for="logicalOperatorAnd" i18n>All</label>
 | 
					          <label class="btn btn-outline-primary" for="logicalOperatorAnd_{{name}}" i18n>All</label>
 | 
				
			||||||
          <input [(ngModel)]="selectionModel.logicalOperator" [disabled]="!operatorToggleEnabled" (ngModelChange)="selectionModel.toggleOperator()" type="radio" class="btn-check" id="logicalOperatorOr" value="or">
 | 
					          <input [(ngModel)]="selectionModel.logicalOperator" [disabled]="!modifierToggleEnabled" (ngModelChange)="selectionModel.toggleOperator()" type="radio" class="btn-check" id="logicalOperatorOr_{{name}}" name="logicalOperatorOr_{{name}}" value="or">
 | 
				
			||||||
          <label class="btn btn-outline-primary" for="logicalOperatorOr" i18n>Any</label>
 | 
					          <label class="btn btn-outline-primary" for="logicalOperatorOr_{{name}}" i18n>Any</label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div *ngIf="!editing && !manyToOne" class="list-group-item d-flex">
 | 
				
			||||||
 | 
					        <div class="btn-group btn-group-xs flex-fill" role="group">
 | 
				
			||||||
 | 
					          <input [(ngModel)]="selectionModel.intersection" [disabled]="!modifierToggleEnabled" (ngModelChange)="selectionModel.toggleIntersection()" type="radio" class="btn-check" id="intersectionInclude_{{name}}" name="intersectionInclude_{{name}}" value="include">
 | 
				
			||||||
 | 
					          <label class="btn btn-outline-primary" for="intersectionInclude_{{name}}" i18n>Include</label>
 | 
				
			||||||
 | 
					          <input [(ngModel)]="selectionModel.intersection" [disabled]="!modifierToggleEnabled" (ngModelChange)="selectionModel.toggleIntersection()" type="radio" class="btn-check" id="intersectionExclude_{{name}}" name="intersectionExclude_{{name}}" value="exclude">
 | 
				
			||||||
 | 
					          <label class="btn btn-outline-primary" for="intersectionExclude_{{name}}" i18n>Exclude</label>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="list-group-item">
 | 
					      <div class="list-group-item">
 | 
				
			||||||
@@ -34,7 +42,7 @@
 | 
				
			|||||||
          <use xlink:href="assets/bootstrap-icons.svg#arrow-right" />
 | 
					          <use xlink:href="assets/bootstrap-icons.svg#arrow-right" />
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
      </button>
 | 
					      </button>
 | 
				
			||||||
      <div *ngIf="!editing && multiple" class="list-group-item list-group-item-note pt-1 pb-2">
 | 
					      <div *ngIf="!editing && manyToOne" class="list-group-item list-group-item-note pt-1 pb-2">
 | 
				
			||||||
        <small i18n>Click again to exclude items.</small>
 | 
					        <small i18n>Click again to exclude items.</small>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,12 +18,25 @@ export interface ChangedItems {
 | 
				
			|||||||
  itemsToRemove: MatchingModel[]
 | 
					  itemsToRemove: MatchingModel[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum LogicalOperator {
 | 
				
			||||||
 | 
					  And = 'and',
 | 
				
			||||||
 | 
					  Or = 'or',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum Intersection {
 | 
				
			||||||
 | 
					  Include = 'include',
 | 
				
			||||||
 | 
					  Exclude = 'exclude',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FilterableDropdownSelectionModel {
 | 
					export class FilterableDropdownSelectionModel {
 | 
				
			||||||
  changed = new Subject<FilterableDropdownSelectionModel>()
 | 
					  changed = new Subject<FilterableDropdownSelectionModel>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  multiple = false
 | 
					  manyToOne = false
 | 
				
			||||||
  private _logicalOperator = 'and'
 | 
					  singleSelect = false
 | 
				
			||||||
  temporaryLogicalOperator = this._logicalOperator
 | 
					  private _logicalOperator: LogicalOperator = LogicalOperator.And
 | 
				
			||||||
 | 
					  temporaryLogicalOperator: LogicalOperator = this._logicalOperator
 | 
				
			||||||
 | 
					  private _intersection: Intersection = Intersection.Include
 | 
				
			||||||
 | 
					  temporaryIntersection: Intersection = this._intersection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  items: MatchingModel[] = []
 | 
					  items: MatchingModel[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,21 +99,36 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
      (state != ToggleableItemState.Selected &&
 | 
					      (state != ToggleableItemState.Selected &&
 | 
				
			||||||
        state != ToggleableItemState.Excluded)
 | 
					        state != ToggleableItemState.Excluded)
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					      if (this.manyToOne || this.singleSelect) {
 | 
				
			||||||
        this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
 | 
					        this.temporarySelectionStates.set(id, ToggleableItemState.Selected)
 | 
				
			||||||
    } else if (
 | 
					 | 
				
			||||||
      state == ToggleableItemState.Selected ||
 | 
					 | 
				
			||||||
      state == ToggleableItemState.Excluded
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
      this.temporarySelectionStates.delete(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this.multiple) {
 | 
					        if (this.singleSelect) {
 | 
				
			||||||
          for (let key of this.temporarySelectionStates.keys()) {
 | 
					          for (let key of this.temporarySelectionStates.keys()) {
 | 
				
			||||||
            if (key != id) {
 | 
					            if (key != id) {
 | 
				
			||||||
              this.temporarySelectionStates.delete(key)
 | 
					              this.temporarySelectionStates.delete(key)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        let newState =
 | 
				
			||||||
 | 
					          this.intersection == Intersection.Include
 | 
				
			||||||
 | 
					            ? ToggleableItemState.Selected
 | 
				
			||||||
 | 
					            : ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					        if (!id) newState = ToggleableItemState.Selected
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          state == ToggleableItemState.Excluded &&
 | 
				
			||||||
 | 
					          this.intersection == Intersection.Exclude
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          newState = ToggleableItemState.NotSelected
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.temporarySelectionStates.set(id, newState)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (
 | 
				
			||||||
 | 
					      state == ToggleableItemState.Selected ||
 | 
				
			||||||
 | 
					      state == ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      this.temporarySelectionStates.delete(id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!id) {
 | 
					    if (!id) {
 | 
				
			||||||
      for (let key of this.temporarySelectionStates.keys()) {
 | 
					      for (let key of this.temporarySelectionStates.keys()) {
 | 
				
			||||||
@@ -119,20 +147,37 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  exclude(id: number, fireEvent: boolean = true) {
 | 
					  exclude(id: number, fireEvent: boolean = true) {
 | 
				
			||||||
    let state = this.temporarySelectionStates.get(id)
 | 
					    let state = this.temporarySelectionStates.get(id)
 | 
				
			||||||
    if (state == null || state != ToggleableItemState.Excluded) {
 | 
					    if (id && (state == null || state != ToggleableItemState.Excluded)) {
 | 
				
			||||||
      this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
 | 
					      this.temporaryLogicalOperator = this._logicalOperator = this.manyToOne
 | 
				
			||||||
      this.temporaryLogicalOperator = this._logicalOperator = 'and'
 | 
					        ? LogicalOperator.And
 | 
				
			||||||
    } else if (state == ToggleableItemState.Excluded) {
 | 
					        : LogicalOperator.Or
 | 
				
			||||||
      this.temporarySelectionStates.delete(id)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this.multiple) {
 | 
					      if (this.manyToOne || this.singleSelect) {
 | 
				
			||||||
 | 
					        this.temporarySelectionStates.set(id, ToggleableItemState.Excluded)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.singleSelect) {
 | 
				
			||||||
          for (let key of this.temporarySelectionStates.keys()) {
 | 
					          for (let key of this.temporarySelectionStates.keys()) {
 | 
				
			||||||
            if (key != id) {
 | 
					            if (key != id) {
 | 
				
			||||||
              this.temporarySelectionStates.delete(key)
 | 
					              this.temporarySelectionStates.delete(key)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        let newState =
 | 
				
			||||||
 | 
					          this.intersection == Intersection.Include
 | 
				
			||||||
 | 
					            ? ToggleableItemState.Selected
 | 
				
			||||||
 | 
					            : ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          state == ToggleableItemState.Selected &&
 | 
				
			||||||
 | 
					          this.intersection == Intersection.Include
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          newState = ToggleableItemState.NotSelected
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.temporarySelectionStates.set(id, newState)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (!id || state == ToggleableItemState.Excluded) {
 | 
				
			||||||
 | 
					      this.temporarySelectionStates.delete(id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (fireEvent) {
 | 
					    if (fireEvent) {
 | 
				
			||||||
      this.changed.next(this)
 | 
					      this.changed.next(this)
 | 
				
			||||||
@@ -143,11 +188,11 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
    return this.selectionStates.get(id) || ToggleableItemState.NotSelected
 | 
					    return this.selectionStates.get(id) || ToggleableItemState.NotSelected
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get logicalOperator(): string {
 | 
					  get logicalOperator(): LogicalOperator {
 | 
				
			||||||
    return this.temporaryLogicalOperator
 | 
					    return this.temporaryLogicalOperator
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  set logicalOperator(operator: string) {
 | 
					  set logicalOperator(operator: LogicalOperator) {
 | 
				
			||||||
    this.temporaryLogicalOperator = operator
 | 
					    this.temporaryLogicalOperator = operator
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -155,6 +200,26 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
    this.changed.next(this)
 | 
					    this.changed.next(this)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get intersection(): Intersection {
 | 
				
			||||||
 | 
					    return this.temporaryIntersection
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set intersection(intersection: Intersection) {
 | 
				
			||||||
 | 
					    this.temporaryIntersection = intersection
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toggleIntersection() {
 | 
				
			||||||
 | 
					    if (this.temporarySelectionStates.size === 0) return
 | 
				
			||||||
 | 
					    let newState =
 | 
				
			||||||
 | 
					      this.intersection == Intersection.Include
 | 
				
			||||||
 | 
					        ? ToggleableItemState.Selected
 | 
				
			||||||
 | 
					        : ToggleableItemState.Excluded
 | 
				
			||||||
 | 
					    this.temporarySelectionStates.forEach((state, key) => {
 | 
				
			||||||
 | 
					      this.temporarySelectionStates.set(key, newState)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    this.changed.next(this)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get(id: number) {
 | 
					  get(id: number) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
 | 
					      this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected
 | 
				
			||||||
@@ -171,7 +236,8 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  clear(fireEvent = true) {
 | 
					  clear(fireEvent = true) {
 | 
				
			||||||
    this.temporarySelectionStates.clear()
 | 
					    this.temporarySelectionStates.clear()
 | 
				
			||||||
    this.temporaryLogicalOperator = this._logicalOperator = 'and'
 | 
					    this.temporaryLogicalOperator = this._logicalOperator = LogicalOperator.And
 | 
				
			||||||
 | 
					    this.temporaryIntersection = this._intersection = Intersection.Include
 | 
				
			||||||
    if (fireEvent) {
 | 
					    if (fireEvent) {
 | 
				
			||||||
      this.changed.next(this)
 | 
					      this.changed.next(this)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -194,6 +260,8 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
      return true
 | 
					      return true
 | 
				
			||||||
    } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
 | 
					    } else if (this.temporaryLogicalOperator !== this._logicalOperator) {
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
 | 
					    } else if (this.temporaryIntersection !== this._intersection) {
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -217,14 +285,19 @@ export class FilterableDropdownSelectionModel {
 | 
				
			|||||||
      this.selectionStates.set(key, value)
 | 
					      this.selectionStates.set(key, value)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    this._logicalOperator = this.temporaryLogicalOperator
 | 
					    this._logicalOperator = this.temporaryLogicalOperator
 | 
				
			||||||
 | 
					    this._intersection = this.temporaryIntersection
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reset() {
 | 
					  reset(complete: boolean = false) {
 | 
				
			||||||
    this.temporarySelectionStates.clear()
 | 
					    this.temporarySelectionStates.clear()
 | 
				
			||||||
 | 
					    if (complete) {
 | 
				
			||||||
 | 
					      this.selectionStates.clear()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
      this.selectionStates.forEach((value, key) => {
 | 
					      this.selectionStates.forEach((value, key) => {
 | 
				
			||||||
        this.temporarySelectionStates.set(key, value)
 | 
					        this.temporarySelectionStates.set(key, value)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  diff(): ChangedItems {
 | 
					  diff(): ChangedItems {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@@ -269,14 +342,16 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
    return this._selectionModel.items
 | 
					    return this._selectionModel.items
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _selectionModel = new FilterableDropdownSelectionModel()
 | 
					  _selectionModel: FilterableDropdownSelectionModel =
 | 
				
			||||||
 | 
					    new FilterableDropdownSelectionModel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  set selectionModel(model: FilterableDropdownSelectionModel) {
 | 
					  set selectionModel(model: FilterableDropdownSelectionModel) {
 | 
				
			||||||
    if (this.selectionModel) {
 | 
					    if (this.selectionModel) {
 | 
				
			||||||
      this.selectionModel.changed.complete()
 | 
					      this.selectionModel.changed.complete()
 | 
				
			||||||
      model.items = this.selectionModel.items
 | 
					      model.items = this.selectionModel.items
 | 
				
			||||||
      model.multiple = this.selectionModel.multiple
 | 
					      model.manyToOne = this.selectionModel.manyToOne
 | 
				
			||||||
 | 
					      model.singleSelect = this.editing && !this.selectionModel.manyToOne
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    model.changed.subscribe((updatedModel) => {
 | 
					    model.changed.subscribe((updatedModel) => {
 | 
				
			||||||
      this.selectionModelChange.next(updatedModel)
 | 
					      this.selectionModelChange.next(updatedModel)
 | 
				
			||||||
@@ -292,12 +367,12 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  selectionModelChange = new EventEmitter<FilterableDropdownSelectionModel>()
 | 
					  selectionModelChange = new EventEmitter<FilterableDropdownSelectionModel>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  set multiple(value: boolean) {
 | 
					  set manyToOne(manyToOne: boolean) {
 | 
				
			||||||
    this.selectionModel.multiple = value
 | 
					    this.selectionModel.manyToOne = manyToOne
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get multiple() {
 | 
					  get manyToOne() {
 | 
				
			||||||
    return this.selectionModel.multiple
 | 
					    return this.selectionModel.manyToOne
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
@@ -327,16 +402,20 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  opened = new EventEmitter()
 | 
					  opened = new EventEmitter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get operatorToggleEnabled(): boolean {
 | 
					  get modifierToggleEnabled(): boolean {
 | 
				
			||||||
    return (
 | 
					    return this.manyToOne
 | 
				
			||||||
      this.selectionModel.selectionSize() > 1 &&
 | 
					      ? this.selectionModel.selectionSize() > 1 &&
 | 
				
			||||||
          this.selectionModel.getExcludedItems().length == 0
 | 
					          this.selectionModel.getExcludedItems().length == 0
 | 
				
			||||||
    )
 | 
					      : !this.selectionModel.isNoneSelected()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  documentCounts: SelectionDataItem[]
 | 
					  documentCounts: SelectionDataItem[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get name(): string {
 | 
				
			||||||
 | 
					    return this.title ? this.title.replace(/\s/g, '_').toLowerCase() : null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getUpdatedDocumentCount(id: number) {
 | 
					  getUpdatedDocumentCount(id: number) {
 | 
				
			||||||
    if (this.documentCounts) {
 | 
					    if (this.documentCounts) {
 | 
				
			||||||
      return this.documentCounts.find((c) => c.id === id)?.document_count
 | 
					      return this.documentCounts.find((c) => c.id === id)?.document_count
 | 
				
			||||||
@@ -346,7 +425,6 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  modelIsDirty: boolean = false
 | 
					  modelIsDirty: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private filterPipe: FilterPipe) {
 | 
					  constructor(private filterPipe: FilterPipe) {
 | 
				
			||||||
    this.selectionModel = new FilterableDropdownSelectionModel()
 | 
					 | 
				
			||||||
    this.selectionModelChange.subscribe((updatedModel) => {
 | 
					    this.selectionModelChange.subscribe((updatedModel) => {
 | 
				
			||||||
      this.modelIsDirty = updatedModel.isDirty()
 | 
					      this.modelIsDirty = updatedModel.isDirty()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
@@ -400,7 +478,7 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reset() {
 | 
					  reset() {
 | 
				
			||||||
    this.selectionModel.reset()
 | 
					    this.selectionModel.reset(true)
 | 
				
			||||||
    this.selectionModelChange.emit(this.selectionModel)
 | 
					    this.selectionModelChange.emit(this.selectionModel)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@
 | 
				
			|||||||
        [items]="tags"
 | 
					        [items]="tags"
 | 
				
			||||||
        [disabled]="!userCanEditAll"
 | 
					        [disabled]="!userCanEditAll"
 | 
				
			||||||
        [editing]="true"
 | 
					        [editing]="true"
 | 
				
			||||||
        [multiple]="true"
 | 
					        [manyToOne]="true"
 | 
				
			||||||
        [applyOnClose]="applyOnClose"
 | 
					        [applyOnClose]="applyOnClose"
 | 
				
			||||||
        (opened)="openTagsDropdown()"
 | 
					        (opened)="openTagsDropdown()"
 | 
				
			||||||
        [(selectionModel)]="tagSelectionModel"
 | 
					        [(selectionModel)]="tagSelectionModel"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,19 +14,19 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div class="btn-group flex-fill" role="group">
 | 
					  <div class="btn-group flex-fill" role="group">
 | 
				
			||||||
    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="details" (ngModelChange)="saveDisplayMode()" id="displayModeDetails">
 | 
					    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="details" (ngModelChange)="saveDisplayMode()" id="displayModeDetails" name="displayModeDetails">
 | 
				
			||||||
    <label for="displayModeDetails" class="btn btn-outline-primary btn-sm">
 | 
					    <label for="displayModeDetails" class="btn btn-outline-primary btn-sm">
 | 
				
			||||||
      <svg class="toolbaricon" fill="currentColor">
 | 
					      <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
        <use xlink:href="assets/bootstrap-icons.svg#list-ul" />
 | 
					        <use xlink:href="assets/bootstrap-icons.svg#list-ul" />
 | 
				
			||||||
      </svg>
 | 
					      </svg>
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="smallCards" (ngModelChange)="saveDisplayMode()" id="displayModeSmall">
 | 
					    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="smallCards" (ngModelChange)="saveDisplayMode()" id="displayModeSmall" name="displayModeSmall">
 | 
				
			||||||
    <label for="displayModeSmall" class="btn btn-outline-primary btn-sm">
 | 
					    <label for="displayModeSmall" class="btn btn-outline-primary btn-sm">
 | 
				
			||||||
      <svg class="toolbaricon" fill="currentColor">
 | 
					      <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
        <use xlink:href="assets/bootstrap-icons.svg#grid" />
 | 
					        <use xlink:href="assets/bootstrap-icons.svg#grid" />
 | 
				
			||||||
      </svg>
 | 
					      </svg>
 | 
				
			||||||
    </label>
 | 
					    </label>
 | 
				
			||||||
    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="largeCards" (ngModelChange)="saveDisplayMode()" id="displayModeLarge">
 | 
					    <input type="radio" class="btn-check" [(ngModel)]="displayMode" value="largeCards" (ngModelChange)="saveDisplayMode()" id="displayModeLarge" name="displayModeLarge">
 | 
				
			||||||
    <label for="displayModeLarge" class="btn btn-outline-primary btn-sm">
 | 
					    <label for="displayModeLarge" class="btn btn-outline-primary btn-sm">
 | 
				
			||||||
      <svg class="toolbaricon" fill="currentColor">
 | 
					      <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
        <use xlink:href="assets/bootstrap-icons.svg#hdd-stack" />
 | 
					        <use xlink:href="assets/bootstrap-icons.svg#hdd-stack" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@
 | 
				
			|||||||
          <app-filterable-dropdown class="flex-fill" title="Tags" icon="tag-fill" i18n-title
 | 
					          <app-filterable-dropdown class="flex-fill" title="Tags" icon="tag-fill" i18n-title
 | 
				
			||||||
            filterPlaceholder="Filter tags" i18n-filterPlaceholder
 | 
					            filterPlaceholder="Filter tags" i18n-filterPlaceholder
 | 
				
			||||||
            [items]="tags"
 | 
					            [items]="tags"
 | 
				
			||||||
            [multiple]="true"
 | 
					            [manyToOne]="true"
 | 
				
			||||||
            [(selectionModel)]="tagSelectionModel"
 | 
					            [(selectionModel)]="tagSelectionModel"
 | 
				
			||||||
            (selectionModelChange)="updateRules()"
 | 
					            (selectionModelChange)="updateRules()"
 | 
				
			||||||
            (opened)="onTagsDropdownOpen()"
 | 
					            (opened)="onTagsDropdownOpen()"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,10 +21,10 @@ import {
 | 
				
			|||||||
  FILTER_ADDED_AFTER,
 | 
					  FILTER_ADDED_AFTER,
 | 
				
			||||||
  FILTER_ADDED_BEFORE,
 | 
					  FILTER_ADDED_BEFORE,
 | 
				
			||||||
  FILTER_ASN,
 | 
					  FILTER_ASN,
 | 
				
			||||||
  FILTER_CORRESPONDENT,
 | 
					  FILTER_HAS_CORRESPONDENT_ANY,
 | 
				
			||||||
  FILTER_CREATED_AFTER,
 | 
					  FILTER_CREATED_AFTER,
 | 
				
			||||||
  FILTER_CREATED_BEFORE,
 | 
					  FILTER_CREATED_BEFORE,
 | 
				
			||||||
  FILTER_DOCUMENT_TYPE,
 | 
					  FILTER_HAS_DOCUMENT_TYPE_ANY,
 | 
				
			||||||
  FILTER_FULLTEXT_MORELIKE,
 | 
					  FILTER_FULLTEXT_MORELIKE,
 | 
				
			||||||
  FILTER_FULLTEXT_QUERY,
 | 
					  FILTER_FULLTEXT_QUERY,
 | 
				
			||||||
  FILTER_HAS_ANY_TAG,
 | 
					  FILTER_HAS_ANY_TAG,
 | 
				
			||||||
@@ -33,12 +33,22 @@ import {
 | 
				
			|||||||
  FILTER_DOES_NOT_HAVE_TAG,
 | 
					  FILTER_DOES_NOT_HAVE_TAG,
 | 
				
			||||||
  FILTER_TITLE,
 | 
					  FILTER_TITLE,
 | 
				
			||||||
  FILTER_TITLE_CONTENT,
 | 
					  FILTER_TITLE_CONTENT,
 | 
				
			||||||
  FILTER_STORAGE_PATH,
 | 
					  FILTER_HAS_STORAGE_PATH_ANY,
 | 
				
			||||||
  FILTER_ASN_ISNULL,
 | 
					  FILTER_ASN_ISNULL,
 | 
				
			||||||
  FILTER_ASN_GT,
 | 
					  FILTER_ASN_GT,
 | 
				
			||||||
  FILTER_ASN_LT,
 | 
					  FILTER_ASN_LT,
 | 
				
			||||||
 | 
					  FILTER_DOES_NOT_HAVE_CORRESPONDENT,
 | 
				
			||||||
 | 
					  FILTER_DOES_NOT_HAVE_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					  FILTER_DOES_NOT_HAVE_STORAGE_PATH,
 | 
				
			||||||
 | 
					  FILTER_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					  FILTER_CORRESPONDENT,
 | 
				
			||||||
 | 
					  FILTER_STORAGE_PATH,
 | 
				
			||||||
} from 'src/app/data/filter-rule-type'
 | 
					} from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'
 | 
					import {
 | 
				
			||||||
 | 
					  FilterableDropdownSelectionModel,
 | 
				
			||||||
 | 
					  Intersection,
 | 
				
			||||||
 | 
					  LogicalOperator,
 | 
				
			||||||
 | 
					} from '../../common/filterable-dropdown/filterable-dropdown.component'
 | 
				
			||||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
					import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  DocumentService,
 | 
					  DocumentService,
 | 
				
			||||||
@@ -93,7 +103,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
    if (this.filterRules.length == 1) {
 | 
					    if (this.filterRules.length == 1) {
 | 
				
			||||||
      let rule = this.filterRules[0]
 | 
					      let rule = this.filterRules[0]
 | 
				
			||||||
      switch (this.filterRules[0].rule_type) {
 | 
					      switch (this.filterRules[0].rule_type) {
 | 
				
			||||||
        case FILTER_CORRESPONDENT:
 | 
					        case FILTER_HAS_CORRESPONDENT_ANY:
 | 
				
			||||||
          if (rule.value) {
 | 
					          if (rule.value) {
 | 
				
			||||||
            return $localize`Correspondent: ${
 | 
					            return $localize`Correspondent: ${
 | 
				
			||||||
              this.correspondents.find((c) => c.id == +rule.value)?.name
 | 
					              this.correspondents.find((c) => c.id == +rule.value)?.name
 | 
				
			||||||
@@ -102,7 +112,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
            return $localize`Without correspondent`
 | 
					            return $localize`Without correspondent`
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case FILTER_DOCUMENT_TYPE:
 | 
					        case FILTER_HAS_DOCUMENT_TYPE_ANY:
 | 
				
			||||||
          if (rule.value) {
 | 
					          if (rule.value) {
 | 
				
			||||||
            return $localize`Type: ${
 | 
					            return $localize`Type: ${
 | 
				
			||||||
              this.documentTypes.find((dt) => dt.id == +rule.value)?.name
 | 
					              this.documentTypes.find((dt) => dt.id == +rule.value)?.name
 | 
				
			||||||
@@ -335,6 +345,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          this.dateAddedBefore = rule.value
 | 
					          this.dateAddedBefore = rule.value
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_HAS_TAGS_ALL:
 | 
					        case FILTER_HAS_TAGS_ALL:
 | 
				
			||||||
 | 
					          this.tagSelectionModel.logicalOperator = LogicalOperator.And
 | 
				
			||||||
          this.tagSelectionModel.set(
 | 
					          this.tagSelectionModel.set(
 | 
				
			||||||
            rule.value ? +rule.value : null,
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
            ToggleableItemState.Selected,
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
@@ -342,7 +353,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          )
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_HAS_TAGS_ANY:
 | 
					        case FILTER_HAS_TAGS_ANY:
 | 
				
			||||||
          this.tagSelectionModel.logicalOperator = 'or'
 | 
					          this.tagSelectionModel.logicalOperator = LogicalOperator.Or
 | 
				
			||||||
          this.tagSelectionModel.set(
 | 
					          this.tagSelectionModel.set(
 | 
				
			||||||
            rule.value ? +rule.value : null,
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
            ToggleableItemState.Selected,
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
@@ -360,26 +371,59 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          )
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
        case FILTER_CORRESPONDENT:
 | 
					        case FILTER_CORRESPONDENT:
 | 
				
			||||||
 | 
					        case FILTER_HAS_CORRESPONDENT_ANY:
 | 
				
			||||||
 | 
					          this.correspondentSelectionModel.logicalOperator = LogicalOperator.Or
 | 
				
			||||||
 | 
					          this.correspondentSelectionModel.intersection = Intersection.Include
 | 
				
			||||||
          this.correspondentSelectionModel.set(
 | 
					          this.correspondentSelectionModel.set(
 | 
				
			||||||
            rule.value ? +rule.value : null,
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
            ToggleableItemState.Selected,
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
            false
 | 
					            false
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
 | 
					        case FILTER_DOES_NOT_HAVE_CORRESPONDENT:
 | 
				
			||||||
 | 
					          this.correspondentSelectionModel.intersection = Intersection.Exclude
 | 
				
			||||||
 | 
					          this.correspondentSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Excluded,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
        case FILTER_DOCUMENT_TYPE:
 | 
					        case FILTER_DOCUMENT_TYPE:
 | 
				
			||||||
 | 
					        case FILTER_HAS_DOCUMENT_TYPE_ANY:
 | 
				
			||||||
 | 
					          this.documentTypeSelectionModel.logicalOperator = LogicalOperator.Or
 | 
				
			||||||
 | 
					          this.documentTypeSelectionModel.intersection = Intersection.Include
 | 
				
			||||||
          this.documentTypeSelectionModel.set(
 | 
					          this.documentTypeSelectionModel.set(
 | 
				
			||||||
            rule.value ? +rule.value : null,
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
            ToggleableItemState.Selected,
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
            false
 | 
					            false
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
 | 
					        case FILTER_DOES_NOT_HAVE_DOCUMENT_TYPE:
 | 
				
			||||||
 | 
					          this.documentTypeSelectionModel.intersection = Intersection.Exclude
 | 
				
			||||||
 | 
					          this.documentTypeSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Excluded,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
        case FILTER_STORAGE_PATH:
 | 
					        case FILTER_STORAGE_PATH:
 | 
				
			||||||
 | 
					        case FILTER_HAS_STORAGE_PATH_ANY:
 | 
				
			||||||
 | 
					          this.storagePathSelectionModel.logicalOperator = LogicalOperator.Or
 | 
				
			||||||
 | 
					          this.storagePathSelectionModel.intersection = Intersection.Include
 | 
				
			||||||
          this.storagePathSelectionModel.set(
 | 
					          this.storagePathSelectionModel.set(
 | 
				
			||||||
            rule.value ? +rule.value : null,
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
            ToggleableItemState.Selected,
 | 
					            ToggleableItemState.Selected,
 | 
				
			||||||
            false
 | 
					            false
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
 | 
					        case FILTER_DOES_NOT_HAVE_STORAGE_PATH:
 | 
				
			||||||
 | 
					          this.storagePathSelectionModel.intersection = Intersection.Exclude
 | 
				
			||||||
 | 
					          this.storagePathSelectionModel.set(
 | 
				
			||||||
 | 
					            rule.value ? +rule.value : null,
 | 
				
			||||||
 | 
					            ToggleableItemState.Excluded,
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
        case FILTER_ASN_ISNULL:
 | 
					        case FILTER_ASN_ISNULL:
 | 
				
			||||||
          this.textFilterTarget = TEXT_FILTER_TARGET_ASN
 | 
					          this.textFilterTarget = TEXT_FILTER_TARGET_ASN
 | 
				
			||||||
          this.textFilterModifier =
 | 
					          this.textFilterModifier =
 | 
				
			||||||
@@ -469,7 +513,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      filterRules.push({ rule_type: FILTER_HAS_ANY_TAG, value: 'false' })
 | 
					      filterRules.push({ rule_type: FILTER_HAS_ANY_TAG, value: 'false' })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const tagFilterType =
 | 
					      const tagFilterType =
 | 
				
			||||||
        this.tagSelectionModel.logicalOperator == 'and'
 | 
					        this.tagSelectionModel.logicalOperator == LogicalOperator.And
 | 
				
			||||||
          ? FILTER_HAS_TAGS_ALL
 | 
					          ? FILTER_HAS_TAGS_ALL
 | 
				
			||||||
          : FILTER_HAS_TAGS_ANY
 | 
					          : FILTER_HAS_TAGS_ANY
 | 
				
			||||||
      this.tagSelectionModel
 | 
					      this.tagSelectionModel
 | 
				
			||||||
@@ -491,28 +535,66 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
          })
 | 
					          })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.correspondentSelectionModel.isNoneSelected()) {
 | 
				
			||||||
 | 
					      filterRules.push({ rule_type: FILTER_CORRESPONDENT, value: null })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
      this.correspondentSelectionModel
 | 
					      this.correspondentSelectionModel
 | 
				
			||||||
        .getSelectedItems()
 | 
					        .getSelectedItems()
 | 
				
			||||||
        .forEach((correspondent) => {
 | 
					        .forEach((correspondent) => {
 | 
				
			||||||
          filterRules.push({
 | 
					          filterRules.push({
 | 
				
			||||||
          rule_type: FILTER_CORRESPONDENT,
 | 
					            rule_type: FILTER_HAS_CORRESPONDENT_ANY,
 | 
				
			||||||
            value: correspondent.id?.toString(),
 | 
					            value: correspondent.id?.toString(),
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					      this.correspondentSelectionModel
 | 
				
			||||||
 | 
					        .getExcludedItems()
 | 
				
			||||||
 | 
					        .forEach((correspondent) => {
 | 
				
			||||||
 | 
					          filterRules.push({
 | 
				
			||||||
 | 
					            rule_type: FILTER_DOES_NOT_HAVE_CORRESPONDENT,
 | 
				
			||||||
 | 
					            value: correspondent.id?.toString(),
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.documentTypeSelectionModel.isNoneSelected()) {
 | 
				
			||||||
 | 
					      filterRules.push({ rule_type: FILTER_DOCUMENT_TYPE, value: null })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
      this.documentTypeSelectionModel
 | 
					      this.documentTypeSelectionModel
 | 
				
			||||||
        .getSelectedItems()
 | 
					        .getSelectedItems()
 | 
				
			||||||
        .forEach((documentType) => {
 | 
					        .forEach((documentType) => {
 | 
				
			||||||
          filterRules.push({
 | 
					          filterRules.push({
 | 
				
			||||||
          rule_type: FILTER_DOCUMENT_TYPE,
 | 
					            rule_type: FILTER_HAS_DOCUMENT_TYPE_ANY,
 | 
				
			||||||
            value: documentType.id?.toString(),
 | 
					            value: documentType.id?.toString(),
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    this.storagePathSelectionModel.getSelectedItems().forEach((storagePath) => {
 | 
					      this.documentTypeSelectionModel
 | 
				
			||||||
 | 
					        .getExcludedItems()
 | 
				
			||||||
 | 
					        .forEach((documentType) => {
 | 
				
			||||||
          filterRules.push({
 | 
					          filterRules.push({
 | 
				
			||||||
        rule_type: FILTER_STORAGE_PATH,
 | 
					            rule_type: FILTER_DOES_NOT_HAVE_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					            value: documentType.id?.toString(),
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.storagePathSelectionModel.isNoneSelected()) {
 | 
				
			||||||
 | 
					      filterRules.push({ rule_type: FILTER_STORAGE_PATH, value: null })
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.storagePathSelectionModel
 | 
				
			||||||
 | 
					        .getSelectedItems()
 | 
				
			||||||
 | 
					        .forEach((storagePath) => {
 | 
				
			||||||
 | 
					          filterRules.push({
 | 
				
			||||||
 | 
					            rule_type: FILTER_HAS_STORAGE_PATH_ANY,
 | 
				
			||||||
            value: storagePath.id?.toString(),
 | 
					            value: storagePath.id?.toString(),
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					      this.storagePathSelectionModel
 | 
				
			||||||
 | 
					        .getExcludedItems()
 | 
				
			||||||
 | 
					        .forEach((storagePath) => {
 | 
				
			||||||
 | 
					          filterRules.push({
 | 
				
			||||||
 | 
					            rule_type: FILTER_DOES_NOT_HAVE_STORAGE_PATH,
 | 
				
			||||||
 | 
					            value: storagePath.id?.toString(),
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (this.dateCreatedBefore) {
 | 
					    if (this.dateCreatedBefore) {
 | 
				
			||||||
      filterRules.push({
 | 
					      filterRules.push({
 | 
				
			||||||
        rule_type: FILTER_CREATED_BEFORE,
 | 
					        rule_type: FILTER_CREATED_BEFORE,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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_CORRESPONDENT } from 'src/app/data/filter-rule-type'
 | 
					import { FILTER_HAS_CORRESPONDENT_ANY } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
					import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
 | 
				
			||||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
 | 
					import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
 | 
				
			||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
					import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
				
			||||||
@@ -35,7 +35,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Paperles
 | 
				
			|||||||
      toastService,
 | 
					      toastService,
 | 
				
			||||||
      documentListViewService,
 | 
					      documentListViewService,
 | 
				
			||||||
      permissionsService,
 | 
					      permissionsService,
 | 
				
			||||||
      FILTER_CORRESPONDENT,
 | 
					      FILTER_HAS_CORRESPONDENT_ANY,
 | 
				
			||||||
      $localize`correspondent`,
 | 
					      $localize`correspondent`,
 | 
				
			||||||
      $localize`correspondents`,
 | 
					      $localize`correspondents`,
 | 
				
			||||||
      PermissionType.Correspondent,
 | 
					      PermissionType.Correspondent,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'
 | 
					import { FILTER_HAS_DOCUMENT_TYPE_ANY } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
					import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
 | 
				
			||||||
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 DocumentTypeListComponent extends ManagementListComponent<Paperless
 | 
				
			|||||||
      toastService,
 | 
					      toastService,
 | 
				
			||||||
      documentListViewService,
 | 
					      documentListViewService,
 | 
				
			||||||
      permissionsService,
 | 
					      permissionsService,
 | 
				
			||||||
      FILTER_DOCUMENT_TYPE,
 | 
					      FILTER_HAS_DOCUMENT_TYPE_ANY,
 | 
				
			||||||
      $localize`document type`,
 | 
					      $localize`document type`,
 | 
				
			||||||
      $localize`document types`,
 | 
					      $localize`document types`,
 | 
				
			||||||
      PermissionType.DocumentType,
 | 
					      PermissionType.DocumentType,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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_STORAGE_PATH } from 'src/app/data/filter-rule-type'
 | 
					import { FILTER_HAS_STORAGE_PATH_ANY } from 'src/app/data/filter-rule-type'
 | 
				
			||||||
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
 | 
					import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
 | 
				
			||||||
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 StoragePathListComponent extends ManagementListComponent<PaperlessS
 | 
				
			|||||||
      toastService,
 | 
					      toastService,
 | 
				
			||||||
      documentListViewService,
 | 
					      documentListViewService,
 | 
				
			||||||
      permissionsService,
 | 
					      permissionsService,
 | 
				
			||||||
      FILTER_STORAGE_PATH,
 | 
					      FILTER_HAS_STORAGE_PATH_ANY,
 | 
				
			||||||
      $localize`storage path`,
 | 
					      $localize`storage path`,
 | 
				
			||||||
      $localize`storage paths`,
 | 
					      $localize`storage paths`,
 | 
				
			||||||
      PermissionType.StoragePath,
 | 
					      PermissionType.StoragePath,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,12 @@ export const FILTER_ASN_GT = 23
 | 
				
			|||||||
export const FILTER_ASN_LT = 24
 | 
					export const FILTER_ASN_LT = 24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_CORRESPONDENT = 3
 | 
					export const FILTER_CORRESPONDENT = 3
 | 
				
			||||||
 | 
					export const FILTER_HAS_CORRESPONDENT_ANY = 26
 | 
				
			||||||
 | 
					export const FILTER_DOES_NOT_HAVE_CORRESPONDENT = 27
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_DOCUMENT_TYPE = 4
 | 
					export const FILTER_DOCUMENT_TYPE = 4
 | 
				
			||||||
 | 
					export const FILTER_HAS_DOCUMENT_TYPE_ANY = 28
 | 
				
			||||||
 | 
					export const FILTER_DOES_NOT_HAVE_DOCUMENT_TYPE = 29
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_IS_IN_INBOX = 5
 | 
					export const FILTER_IS_IN_INBOX = 5
 | 
				
			||||||
export const FILTER_HAS_TAGS_ALL = 6
 | 
					export const FILTER_HAS_TAGS_ALL = 6
 | 
				
			||||||
@@ -18,6 +22,8 @@ export const FILTER_DOES_NOT_HAVE_TAG = 17
 | 
				
			|||||||
export const FILTER_HAS_TAGS_ANY = 22
 | 
					export const FILTER_HAS_TAGS_ANY = 22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_STORAGE_PATH = 25
 | 
					export const FILTER_STORAGE_PATH = 25
 | 
				
			||||||
 | 
					export const FILTER_HAS_STORAGE_PATH_ANY = 30
 | 
				
			||||||
 | 
					export const FILTER_DOES_NOT_HAVE_STORAGE_PATH = 31
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FILTER_CREATED_BEFORE = 8
 | 
					export const FILTER_CREATED_BEFORE = 8
 | 
				
			||||||
export const FILTER_CREATED_AFTER = 9
 | 
					export const FILTER_CREATED_AFTER = 9
 | 
				
			||||||
@@ -63,6 +69,18 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
 | 
				
			|||||||
    datatype: 'correspondent',
 | 
					    datatype: 'correspondent',
 | 
				
			||||||
    multi: false,
 | 
					    multi: false,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_CORRESPONDENT_ANY,
 | 
				
			||||||
 | 
					    filtervar: 'correspondent__id__in',
 | 
				
			||||||
 | 
					    datatype: 'correspondent',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_DOES_NOT_HAVE_CORRESPONDENT,
 | 
				
			||||||
 | 
					    filtervar: 'correspondent__id__none',
 | 
				
			||||||
 | 
					    datatype: 'correspondent',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    id: FILTER_STORAGE_PATH,
 | 
					    id: FILTER_STORAGE_PATH,
 | 
				
			||||||
    filtervar: 'storage_path__id',
 | 
					    filtervar: 'storage_path__id',
 | 
				
			||||||
@@ -70,6 +88,18 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
 | 
				
			|||||||
    datatype: 'storage_path',
 | 
					    datatype: 'storage_path',
 | 
				
			||||||
    multi: false,
 | 
					    multi: false,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_STORAGE_PATH_ANY,
 | 
				
			||||||
 | 
					    filtervar: 'storage_path__id__in',
 | 
				
			||||||
 | 
					    datatype: 'storage_path',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_DOES_NOT_HAVE_STORAGE_PATH,
 | 
				
			||||||
 | 
					    filtervar: 'storage_path__id__none',
 | 
				
			||||||
 | 
					    datatype: 'storage_path',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    id: FILTER_DOCUMENT_TYPE,
 | 
					    id: FILTER_DOCUMENT_TYPE,
 | 
				
			||||||
    filtervar: 'document_type__id',
 | 
					    filtervar: 'document_type__id',
 | 
				
			||||||
@@ -77,6 +107,18 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
 | 
				
			|||||||
    datatype: 'document_type',
 | 
					    datatype: 'document_type',
 | 
				
			||||||
    multi: false,
 | 
					    multi: false,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_HAS_DOCUMENT_TYPE_ANY,
 | 
				
			||||||
 | 
					    filtervar: 'document_type__id__in',
 | 
				
			||||||
 | 
					    datatype: 'document_type',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: FILTER_DOES_NOT_HAVE_DOCUMENT_TYPE,
 | 
				
			||||||
 | 
					    filtervar: 'document_type__id__none',
 | 
				
			||||||
 | 
					    datatype: 'document_type',
 | 
				
			||||||
 | 
					    multi: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    id: FILTER_IS_IN_INBOX,
 | 
					    id: FILTER_IS_IN_INBOX,
 | 
				
			||||||
    filtervar: 'is_in_inbox',
 | 
					    filtervar: 'is_in_inbox',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,12 +86,12 @@ export function queryParamsFromFilterRules(filterRules: FilterRule[]): Params {
 | 
				
			|||||||
    let params = {}
 | 
					    let params = {}
 | 
				
			||||||
    for (let rule of filterRules) {
 | 
					    for (let rule of filterRules) {
 | 
				
			||||||
      let ruleType = FILTER_RULE_TYPES.find((t) => t.id == rule.rule_type)
 | 
					      let ruleType = FILTER_RULE_TYPES.find((t) => t.id == rule.rule_type)
 | 
				
			||||||
      if (ruleType.multi) {
 | 
					      if (ruleType.isnull_filtervar && rule.value == null) {
 | 
				
			||||||
 | 
					        params[ruleType.isnull_filtervar] = 1
 | 
				
			||||||
 | 
					      } else if (ruleType.multi) {
 | 
				
			||||||
        params[ruleType.filtervar] = params[ruleType.filtervar]
 | 
					        params[ruleType.filtervar] = params[ruleType.filtervar]
 | 
				
			||||||
          ? params[ruleType.filtervar] + ',' + rule.value
 | 
					          ? params[ruleType.filtervar] + ',' + rule.value
 | 
				
			||||||
          : rule.value
 | 
					          : rule.value
 | 
				
			||||||
      } else if (ruleType.isnull_filtervar && rule.value == null) {
 | 
					 | 
				
			||||||
        params[ruleType.isnull_filtervar] = 1
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        params[ruleType.filtervar] = rule.value
 | 
					        params[ruleType.filtervar] = rule.value
 | 
				
			||||||
        if (ruleType.datatype == 'boolean')
 | 
					        if (ruleType.datatype == 'boolean')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,29 +36,30 @@ class DocumentTypeFilterSet(FilterSet):
 | 
				
			|||||||
        fields = {"name": CHAR_KWARGS}
 | 
					        fields = {"name": CHAR_KWARGS}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagsFilter(Filter):
 | 
					class ObjectFilter(Filter):
 | 
				
			||||||
    def __init__(self, exclude=False, in_list=False):
 | 
					    def __init__(self, exclude=False, in_list=False, field_name=""):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
        self.exclude = exclude
 | 
					        self.exclude = exclude
 | 
				
			||||||
        self.in_list = in_list
 | 
					        self.in_list = in_list
 | 
				
			||||||
 | 
					        self.field_name = field_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def filter(self, qs, value):
 | 
					    def filter(self, qs, value):
 | 
				
			||||||
        if not value:
 | 
					        if not value:
 | 
				
			||||||
            return qs
 | 
					            return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            tag_ids = [int(x) for x in value.split(",")]
 | 
					            object_ids = [int(x) for x in value.split(",")]
 | 
				
			||||||
        except ValueError:
 | 
					        except ValueError:
 | 
				
			||||||
            return qs
 | 
					            return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.in_list:
 | 
					        if self.in_list:
 | 
				
			||||||
            qs = qs.filter(tags__id__in=tag_ids).distinct()
 | 
					            qs = qs.filter(**{f"{self.field_name}__id__in": object_ids}).distinct()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            for tag_id in tag_ids:
 | 
					            for obj_id in object_ids:
 | 
				
			||||||
                if self.exclude:
 | 
					                if self.exclude:
 | 
				
			||||||
                    qs = qs.exclude(tags__id=tag_id)
 | 
					                    qs = qs.exclude(**{f"{self.field_name}__id": obj_id})
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    qs = qs.filter(tags__id=tag_id)
 | 
					                    qs = qs.filter(**{f"{self.field_name}__id": obj_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return qs
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -90,11 +91,17 @@ class DocumentFilterSet(FilterSet):
 | 
				
			|||||||
        exclude=True,
 | 
					        exclude=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tags__id__all = TagsFilter()
 | 
					    tags__id__all = ObjectFilter(field_name="tags")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tags__id__none = TagsFilter(exclude=True)
 | 
					    tags__id__none = ObjectFilter(field_name="tags", exclude=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tags__id__in = TagsFilter(in_list=True)
 | 
					    tags__id__in = ObjectFilter(field_name="tags", in_list=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    correspondent__id__none = ObjectFilter(field_name="correspondent", exclude=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document_type__id__none = ObjectFilter(field_name="document_type", exclude=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    storage_path__id__none = ObjectFilter(field_name="storage_path", exclude=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_in_inbox = InboxFilter()
 | 
					    is_in_inbox = InboxFilter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					# Generated by Django 4.1.5 on 2023-03-15 07:10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("documents", "1033_alter_documenttype_options_alter_tag_options_and_more"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name="savedviewfilterrule",
 | 
				
			||||||
 | 
					            name="rule_type",
 | 
				
			||||||
 | 
					            field=models.PositiveIntegerField(
 | 
				
			||||||
 | 
					                choices=[
 | 
				
			||||||
 | 
					                    (0, "title contains"),
 | 
				
			||||||
 | 
					                    (1, "content contains"),
 | 
				
			||||||
 | 
					                    (2, "ASN is"),
 | 
				
			||||||
 | 
					                    (3, "correspondent is"),
 | 
				
			||||||
 | 
					                    (4, "document type is"),
 | 
				
			||||||
 | 
					                    (5, "is in inbox"),
 | 
				
			||||||
 | 
					                    (6, "has tag"),
 | 
				
			||||||
 | 
					                    (7, "has any tag"),
 | 
				
			||||||
 | 
					                    (8, "created before"),
 | 
				
			||||||
 | 
					                    (9, "created after"),
 | 
				
			||||||
 | 
					                    (10, "created year is"),
 | 
				
			||||||
 | 
					                    (11, "created month is"),
 | 
				
			||||||
 | 
					                    (12, "created day is"),
 | 
				
			||||||
 | 
					                    (13, "added before"),
 | 
				
			||||||
 | 
					                    (14, "added after"),
 | 
				
			||||||
 | 
					                    (15, "modified before"),
 | 
				
			||||||
 | 
					                    (16, "modified after"),
 | 
				
			||||||
 | 
					                    (17, "does not have tag"),
 | 
				
			||||||
 | 
					                    (18, "does not have ASN"),
 | 
				
			||||||
 | 
					                    (19, "title or content contains"),
 | 
				
			||||||
 | 
					                    (20, "fulltext query"),
 | 
				
			||||||
 | 
					                    (21, "more like this"),
 | 
				
			||||||
 | 
					                    (22, "has tags in"),
 | 
				
			||||||
 | 
					                    (23, "ASN greater than"),
 | 
				
			||||||
 | 
					                    (24, "ASN less than"),
 | 
				
			||||||
 | 
					                    (25, "storage path is"),
 | 
				
			||||||
 | 
					                    (26, "has correspondent in"),
 | 
				
			||||||
 | 
					                    (27, "does not have correspondent in"),
 | 
				
			||||||
 | 
					                    (28, "has document type in"),
 | 
				
			||||||
 | 
					                    (29, "does not have document type in"),
 | 
				
			||||||
 | 
					                    (30, "has storage path in"),
 | 
				
			||||||
 | 
					                    (31, "does not have storage path in"),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                verbose_name="rule type",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -447,6 +447,12 @@ class SavedViewFilterRule(models.Model):
 | 
				
			|||||||
        (23, _("ASN greater than")),
 | 
					        (23, _("ASN greater than")),
 | 
				
			||||||
        (24, _("ASN less than")),
 | 
					        (24, _("ASN less than")),
 | 
				
			||||||
        (25, _("storage path is")),
 | 
					        (25, _("storage path is")),
 | 
				
			||||||
 | 
					        (26, _("has correspondent in")),
 | 
				
			||||||
 | 
					        (27, _("does not have correspondent in")),
 | 
				
			||||||
 | 
					        (28, _("has document type in")),
 | 
				
			||||||
 | 
					        (29, _("does not have document type in")),
 | 
				
			||||||
 | 
					        (30, _("has storage path in")),
 | 
				
			||||||
 | 
					        (31, _("does not have storage path in")),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    saved_view = models.ForeignKey(
 | 
					    saved_view = models.ForeignKey(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user