示例#1
0
    def search(
            self,
            # Search parameters
            client_id=None,
            order_by=None,
            fetch_all=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the list with Contact-objects

        :param client_id: ID of the client (mandatory)

        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        """

        # Check empty param
        if not client_id:
            raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/contacts")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameter
        url.query["client_id"] = client_id

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        contacts_etree = ET.fromstring(response.data)

        self.per_page = int(contacts_etree.attrib.get("per_page", "100"))
        self.total = int(contacts_etree.attrib.get("total", "0"))
        self.page = int(contacts_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all contacts
        for contact_etree in contacts_etree:
            self.append(Contact(conn=self.conn, contact_etree=contact_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                client_id=client_id,
                order_by=order_by,
                fetch_all=fetch_all,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
示例#2
0
    def search(
            self,
            # Search parameters
            client_id=None,
            contact_id=None,
            invoice_number=None,
            status=None,
            payment_type=None,
            from_date=None,
            to_date=None,
            label=None,
            intro=None,
            note=None,
            tags=None,
            article_id=None,
            order_by=None,
            fetch_all=False,
            allow_empty_filter=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the list with Invoice-objects

        If no search criteria given --> all invoices will returned (REALLY ALL!).

        :param client_id: ID of the client
        :param contact_id: ID of the contact
        :param invoice_number: invoice number
        :param status: Status (DRAFT, OPEN, PAID, OVERDUE, CANCELED).
            More than one statuses could be given as a comma separated list.
            Theses statuses will be logically OR-connected.
        :param payment_type: Payment Type (eg. CASH, BANK_TRANSFER, PAYPAL, ...).
            More than one payment type could be given as a comma separated list.
            Theses payment types will be logically OR-connected.
            You can find a overview of all payment types at API documentation
            of payments.
        :param from_date: (originaly: "from") Only show invoices since this
            date (format YYYY-MM-DD)
        :param to_date: (originaly: "to") Only show invoices up to this
            date (format YYYY-MM-DD)
        :param label: Free text search in label text
        :param intro: Free text search in introductory text
        :param note: Free text search in explanatory notes
        :param tags: Comma seperated list of tags
        :param article_id: ID of an article
        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            So, all invoices will returned. !!! EVERY INVOICE !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                    client_id,
                    contact_id,
                    invoice_number,
                    status,
                    payment_type,
                    from_date,
                    to_date,
                    label,
                    intro,
                    note,
                    tags,
                    article_id,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/invoices")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if client_id:
            url.query["client_id"] = client_id
        if contact_id:
            url.query["contact_id"] = contact_id
        if invoice_number:
            url.query["invoice_number"] = invoice_number
        if status:
            url.query["status"] = status
        if payment_type:
            url.query["payment_type"] = payment_type
        if from_date:
            url.query["from"] = from_date
        if to_date:
            url.query["to"] = to_date
        if label:
            url.query["label"] = label
        if intro:
            url.query["intro"] = intro
        if note:
            url.query["note"] = note
        if tags:
            url.query["tags"] = tags
        if article_id:
            url.query["article_id"] = article_id

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        invoices_etree = ET.fromstring(response.data)

        self.per_page = int(invoices_etree.attrib.get("per_page", "100"))
        self.total = int(invoices_etree.attrib.get("total", "0"))
        self.page = int(invoices_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all invoices
        for invoice_etree in invoices_etree:
            self.append(Invoice(conn=self.conn, invoice_etree=invoice_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                client_id=client_id,
                contact_id=contact_id,
                invoice_number=invoice_number,
                status=status,
                payment_type=payment_type,
                from_date=from_date,
                to_date=to_date,
                label=label,
                intro=intro,
                note=note,
                tags=tags,
                article_id=article_id,
                order_by=order_by,
                fetch_all=fetch_all,
                allow_empty_filter=allow_empty_filter,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
示例#3
0
    def search(
            self,
            # Search parameters
            reminder_id=None,
            order_by=None,
            fetch_all=False,
            allow_empty_filter=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the (internal) list with ReminderTag-objects

        If no search criteria given --> all tags will returned (REALLY ALL!).

        :param reminder_id: Reminder ID
        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            All reminder-tags will returned. !!! EVERY !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                    reminder_id,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/reminder-tags")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if reminder_id:
            url.query["reminder_id"] = reminder_id

        # Fetch data
        response = self.conn.get(path=str(url))
        if response.status != 200:
            # Check if "Unothorized" --> raise NotFoundError
            errors_etree = ET.fromstring(response.data)
            for error_etree in errors_etree:
                text = error_etree.text
                if text.lower() == "unauthorized":
                    raise errors.NotFoundError(
                        u"reminder_id: {reminder_id}".format(
                            reminder_id=reminder_id))
            # Other Error
            raise errors.BillomatError(response.data)

        # No response (workaround for inconsistent gziped answer; DecodeError)
        try:
            if len(response.data) == 0:
                return
        except urllib3.exceptions.DecodeError:
            if response.headers.get("content-type",
                                    "").lower() != "application/xml":
                return
            else:
                raise

        # Parse XML
        tags_etree = ET.fromstring(response.data)

        self.per_page = int(tags_etree.attrib.get("per_page", "0"))
        self.total = int(tags_etree.attrib.get("total", "0"))
        self.page = int(tags_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all tags
        for tag_etree in tags_etree:
            self.append(ReminderTag(conn=self.conn, tag_etree=tag_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                reminder_id=reminder_id,
                fetch_all=fetch_all,
                allow_empty_filter=allow_empty_filter,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
    def search(
            self,
            # Search parameters
            credit_note_id=None,
            order_by=None,
            fetch_all=False,
            keep_old_items=False,
            page=1,
            per_page=None,
            credit_note_ids=None):
        """
        Fills the list with CreditNoteItem-objects

        :param credit_note_id: ID of the credit note (mandatory) or a list of IDs.
            If list with IDs given: The result contains the credit note items of
            many credit notes. Be careful: Too many credit note IDs can produce to
            large responses or to large SQL statements.
            My recommendation: 10-50 credit note IDs at one time.

        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.
        """

        # Check empty param
        if not credit_note_id:
            raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path=CreditNoteItem.base_path)
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameter
        if isinstance(credit_note_id, (list, tuple)):
            credit_note_id = ",".join(str(id) for id in set(credit_note_id))

        url.query["credit_note_id"] = credit_note_id

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        credit_note_items_etree = ET.fromstring(response.data)

        self.per_page = int(
            credit_note_items_etree.attrib.get("per_page", "100"))
        self.total = int(credit_note_items_etree.attrib.get("total", "0"))
        self.page = int(credit_note_items_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all items
        for credit_note_item_etree in credit_note_items_etree:
            self.append(
                CreditNoteItem(conn=self.conn,
                               credit_note_item_etree=credit_note_item_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                credit_note_id=credit_note_id,
                order_by=order_by,
                fetch_all=fetch_all,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
示例#5
0
    def search(
            self,
            # Search parameters
            client_id=None,
            contact_id=None,
            name=None,
            payment_type=None,
            cycle=None,
            label=None,
            intro=None,
            note=None,
            tags=None,
            order_by=None,
            fetch_all=False,
            allow_empty_filter=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the list with Recurring-objects

        If no search criteria given --> all recurrings will returned (REALLY ALL!).

        :param client_id: ID of the client
        :param contact_id: ID of the contact
        :param name: The Name of the recurring
        :param payment_type: Payment Type (eg. CASH, BANK_TRANSFER, PAYPAL, ...).
            More than one payment type could be given as a comma separated list.
            Theses payment types will be logically OR-connected.
            You can find a overview of all payment types at API documentation
            of payments.
        :param cycle: Interval (DAILY, WEEKLY, MONTHLY, YEARLY).
        :param label: Free text search in label text
        :param intro: Free text search in introductory text
        :param note: Free text search in explanatory notes
        :param tags: Comma seperated list of tags

        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            So, all invoices will returned. !!! EVERY INVOICE !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                    client_id,
                    contact_id,
                    name,
                    payment_type,
                    cycle,
                    label,
                    intro,
                    note,
                    tags,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/recurrings")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if client_id:
            url.query["client_id"] = client_id
        if contact_id:
            url.query["contact_id"] = contact_id
        if name:
            url.query["name"] = name
        if payment_type:
            url.query["payment_type"] = payment_type
        if cycle:
            url.query["cycle"] = cycle
        if label:
            url.query["label"] = label
        if intro:
            url.query["intro"] = intro
        if note:
            url.query["note"] = note
        if tags:
            url.query["tags"] = tags

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        recurrings_etree = ET.fromstring(response.data)

        self.per_page = int(recurrings_etree.attrib.get("per_page", "100"))
        self.total = int(recurrings_etree.attrib.get("total", "0"))
        self.page = int(recurrings_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all recurrings
        for recurring_etree in recurrings_etree:
            self.append(
                Recurring(conn=self.conn, recurring_etree=recurring_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                client_id=client_id,
                contact_id=contact_id,
                name=name,
                payment_type=payment_type,
                cycle=cycle,
                label=label,
                intro=intro,
                note=note,
                tags=tags,
                order_by=order_by,
                fetch_all=fetch_all,
                allow_empty_filter=allow_empty_filter,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
示例#6
0
    def search(
            self,
            # Search parameters
            name=None,
            client_number=None,
            email=None,
            first_name=None,
            last_name=None,
            country_code=None,
            note=None,
            invoice_id=None,
            tags=None,
            order_by=None,
            fetch_all=False,
            allow_empty_filter=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the (internal) list with Client-objects

        If no search criteria given --> all clients will returned (REALLY ALL!).

        :param name: Company name
        :param client_number: Client number
        :param email: E-mail address
        :param first_name: First name of the contact person
        :param last_name: Last name of the contact person
        :param country_code: Country code as ISO 3166 Alpha-2
        :param note: Note
        :param invoice_id: ID of an invoice of this client,
            multiple values seperated with comma
        :param tags: Comma seperated list of tags
        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            All clients will returned. !!! EVERY CLIENT !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                    name,
                    client_number,
                    email,
                    first_name,
                    last_name,
                    country_code,
                    note,
                    invoice_id,
                    tags,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/clients")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if name:
            url.query["name"] = name
        if client_number:
            url.query["client_number"] = client_number
        if email:
            url.query["email"] = email
        if first_name:
            url.query["first_name"] = first_name
        if last_name:
            url.query["last_name"] = last_name
        if country_code:
            url.query["country_code"] = country_code
        if note:
            url.query["note"] = note
        if invoice_id:
            url.query["invoice_id"] = invoice_id
        if tags:
            url.query["tags"] = tags

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        clients_etree = ET.fromstring(response.data)

        self.per_page = int(clients_etree.attrib.get("per_page", "0"))
        self.total = int(clients_etree.attrib.get("total", "0"))
        self.page = int(clients_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all clients
        for client_etree in clients_etree:
            self.append(Client(conn=self.conn, client_etree=client_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                name=name,
                client_number=client_number,
                email=email,
                first_name=first_name,
                last_name=last_name,
                country_code=country_code,
                note=note,
                invoice_id=invoice_id,
                tags=tags,
                order_by=order_by,
                fetch_all=fetch_all,
                allow_empty_filter=allow_empty_filter,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)
示例#7
0
    def search(
        self,
        # Search parameters
        invoice_id = None,
        from_date = None,
        to_date = None,
        type = None,
        user_id = None,
        order_by = None,

        fetch_all = False,
        allow_empty_filter = False,
        keep_old_items = False,
        page = 1,
        per_page = None
    ):
        """
        Fills the (internal) list with InvoicePayment-objects

        If no search criteria given --> all payments will returned (REALLY ALL!).

        :param invoice_id: ID of the invoice

        :param from_date: Original "from"; Only payments since this date

        :param to_date: Original "to"; Only payments up to this date

        :param type: Payment type (eg. CASH, BANK_TRANSFER, PAYPAL, ...).
            More than one payment type could be given as a comma separated list.
            Theses payment types will be logically OR-connected.

        :param user_id: ID of the user

        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            All invoice-payments will returned. !!! EVERY !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                invoice_id,
                from_date,
                to_date,
                type,
                user_id,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path = "/api/invoice-payments")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if invoice_id:
            url.query["invoice_id"] = invoice_id
        if from_date:
            url.query["from"] = from_date
        if to_date:
            url.query["to"] = to_date
        if type:
            url.query["type"] = type
        if user_id:
            url.query["user_id"] = user_id

        # Fetch data
        response = self.conn.get(path = str(url))
        if response.status != 200:
            # Check if "Unothorized" --> raise NotFoundError
            errors_etree = ET.fromstring(response.data)
            for error_etree in errors_etree:
                text = error_etree.text
                if text.lower() == "unauthorized":
                    raise errors.NotFoundError(
                        u"invoice_id: {invoice_id}".format(invoice_id = invoice_id)
                    )
            # Other Error
            raise errors.BillomatError(response.data)

        # No response (workaround for inconsistent gziped answer; DecodeError)
        try:
            if len(response.data) == 0:
                return
        except urllib3.exceptions.DecodeError:
            if response.headers.get("content-type", "").lower() != "application/xml":
                return
            else:
                raise

        # Parse XML
        payments_etree = ET.fromstring(response.data)

        self.per_page = int(payments_etree.attrib.get("per_page", "0"))
        self.total = int(payments_etree.attrib.get("total", "0"))
        self.page = int(payments_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all payments
        for payment_etree in payments_etree:
            self.append(
                InvoicePayment(conn = self.conn, payment_etree = payment_etree)
            )

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                invoice_id = invoice_id,
                from_date = from_date,
                to_date = to_date,
                type = type,
                user_id = user_id,
                order_by = order_by,

                fetch_all = fetch_all,
                allow_empty_filter = allow_empty_filter,
                keep_old_items = True,
                page = page + 1,
                per_page = per_page
            )
示例#8
0
    def search(
            self,
            # Search parameters
            article_number=None,
            title=None,
            description=None,
            currency_code=None,
            unit_id=None,
            tags=None,
            supplier_id=None,
            order_by=None,
            fetch_all=False,
            allow_empty_filter=False,
            keep_old_items=False,
            page=1,
            per_page=None):
        """
        Fills the list with Article-objects

        If no search criteria given --> all articles will returned (REALLY ALL!).

        :param article_number: Article number
        :param title: Title
        :param description: Description
        :param currency_code: ISO code of the currency
        :param unit_id: ID of the chosen unit
        :param tags: Comma seperated list of tags
        :param supplier_id: ID of the chosen supplier

        :param order_by: Sortings consist of the name of the field and
            sort order: ASC for ascending resp. DESC for descending order.
            If no order is specified, ascending order (ASC) is used.
            Nested sort orders are possible. Please separate the sort orders by
            comma.

        :param allow_empty_filter: If `True`, every filter-parameter may be empty.
            So, all articles will returned. !!! EVERY INVOICE !!!
        """

        # Check empty filter
        if not allow_empty_filter:
            if not any([
                    article_number,
                    title,
                    description,
                    currency_code,
                    unit_id,
                    tags,
                    supplier_id,
            ]):
                raise errors.EmptyFilterError()

        # Empty the list
        if not keep_old_items:
            while True:
                try:
                    self.pop()
                except IndexError:
                    break

        # Url and system-parameters
        url = Url(path="/api/articles")
        url.query["page"] = page
        if per_page:
            url.query["per_page"] = per_page
        if order_by:
            url.query["order_by"] = order_by

        # Search parameters
        if article_number:
            url.query["article_number"] = article_number
        if title:
            url.query["title"] = title
        if description:
            url.query["description"] = description
        if currency_code:
            url.query["currency_code"] = currency_code
        if unit_id:
            url.query["unit_id"] = unit_id
        if tags:
            url.query["tags"] = tags
        if supplier_id:
            url.query["supplier_id"] = supplier_id

        # Fetch data
        response = self.conn.get(path=str(url))

        # Parse XML
        articles_etree = ET.fromstring(response.data)

        self.per_page = int(articles_etree.attrib.get("per_page", "100"))
        self.total = int(articles_etree.attrib.get("total", "0"))
        self.page = int(articles_etree.attrib.get("page", "1"))
        try:
            self.pages = (self.total // self.per_page) + int(
                bool(self.total % self.per_page))
        except ZeroDivisionError:
            self.pages = 0

        # Iterate over all articles
        for article_etree in articles_etree:
            self.append(Article(conn=self.conn, article_etree=article_etree))

        # Fetch all
        if fetch_all and self.total > (self.page * self.per_page):
            self.search(
                # Search parameters
                article_number=article_number,
                title=title,
                description=description,
                currency_code=currency_code,
                unit_id=unit_id,
                tags=tags,
                supplier_id=supplier_id,
                order_by=order_by,
                fetch_all=fetch_all,
                allow_empty_filter=allow_empty_filter,
                keep_old_items=True,
                page=page + 1,
                per_page=per_page)