Esempio n. 1
0
def add_variables():
    """
    For **active** process instances of the last version, add the variables:
        actualMeasurementDate
        shippingDate

    We do not add actualInstallationDate since those process instances are
    already completed.
    """
    # get a list of all active processIds, since we can not put variable on
    # already completed process instances
    processes = [p['id'] for p in cc.makeRequest('/process-instance', 'post', {
        'processDefinitionKey': 'worktop'
    })]

    for (activity, var) in (
        ('TakeMeasurement', 'actualMeasurementDate'),
            ('WorktopShipped', 'shippingDate'), ):

        for act in [a for a in cc.makeRequest(
            '/history/activity-instance', 'post', {
                'processDefinitionKey': 'worktop',
                'activityId': activity,
                'finished': True
            }
        ) if not a['canceled']]:
            if act['processInstanceId'] in processes:
                cc.makeRequest(
                    f'/process-instance/{act["processInstanceId"]}'
                    f'/variables/{var}',
                    'put', params={
                        'value': act['endTime'],
                        'type': 'Date'
                    })
Esempio n. 2
0
    def completeTask(self, taskId, taskKey, variables=None):
        """ Complete the given task and change the process variables.

        :return:
            True if task completes successfully.
            False if status 500 is returned by camunda, usually because the
                task is already completed by some other user

        :raises:
            RPCUserError when anything else happens
        """
        # parse all variable fields whose name ends with Date as date
        # we'd better check against a process variable repository to find
        # the type in stead of relying on variable naming
        if variables:
            variables = parseDate(variables,
                                  fields=[
                                      fname for fname in variables
                                      if fname.endswith('Date')
                                  ])

        handler = getattr(self, 'handle' + taskKey, None)
        if handler:
            handler(taskId, variables)

        try:
            cc.makeRequest(f'/task/{taskId}/complete',
                           'post',
                           variables=variables)
        except CamundaRESTError as e:
            if e.status == 500:
                return False
            else:
                raise RPCUserError('无法完成该任务,请联系技术支持')
        return True
Esempio n. 3
0
    def getTask(self, taskId):
        """
        Return a single task by task id together with all process variables of
        the process to which the task belongs.

        If the task can no longer be found, maybe completed by someone else,
        return None.
        """
        try:
            task = cc.makeRequest(f'/task/{taskId}',
                                  'get',
                                  withProcessVariables='*')
        except CamundaRESTError as e:
            if e.status == 404:
                task = None
            else:
                raise

        if task:
            task['comments'] = cc.makeRequest(f'/task/{taskId}/comment', 'get')
            ois = task['processVariables'].get('orderItems')
            if ois:
                addItemInfo(ois)

        return task
Esempio n. 4
0
def do_main():
    ret = cc.makeRequest('/process-instance', 'post',
                         {'processDefinitionKey': 'worktop'})
    for p in ret:
        cc.makeRequest(
            f'/process-instance/{p["id"]}/variables/orderId', 'put', params={
                'value': int(p['businessKey']),
                'type': 'Integer'
            }
        )
Esempio n. 5
0
def migrate_process():
    plan = cc.makeRequest('/migration/generate', 'post', params={
        'sourceProcessDefinitionId': SOURCE_ID,
        'targetProcessDefinitionId': TARGET_ID
    })
    cc.makeRequest('/migration/execute', 'post', params={
        "skipCustomListeners": True,
        "processInstanceQuery": {
            "processDefinitionId": SOURCE_ID
        },
        "migrationPlan": plan
    })
Esempio n. 6
0
    def __call__(self):
        request = self.request

        ret = cc.makeRequest(
            '/variable-instance',
            'post', {'processInstanceIdIn': [request.matchdict['processId']]},
            urlParams={'deserializeValues': 'false'})
        if not ret:
            raise HTTPNotFound()

        self.variables = cc.parseVariables(ret)
        ois = self.variables.get('orderItems')
        if ois:
            addItemInfo(ois)

        stream = self.template.generate(variables=self.variables, view=self)
        body = rml2pdf.parseString(stream.render()).read()

        response = Response(
            content_type='application/pdf',
            content_disposition='filename="%s_%s.pdf"' %
            (self.variables['externalOrderId'], request.matchdict['form']),
            content_length=len(body),
            body=body)
        return response
Esempio n. 7
0
    def getTaskList(self):
        """
        Return all active tasks of a process key. In additional to the
        properties of the Camunda task object, we also return serveral
        additional process variables related to the task, like externalOrderId
        and customerName in a `processVariables` attribute.
        """
        params = {
            'processDefinitionKey':
            'worktop',
            'sorting': [{
                'sortBy': 'name',
                'sortOrder': 'asc'
            }, {
                'sortBy': 'dueDate',
                'sortOrder': 'asc'
            }]
        }
        if not hasRole(self.request.user, 'admin'):
            params['taskDefinitionKeyIn'] = getUserTasks(self.request.user)
        tasks = cc.makeRequest('/task',
                               'post',
                               params,
                               withProcessVariables=('externalOrderId',
                                                     'customerName',
                                                     'customerRegionCode'))
        for t in tasks:
            var = t['processVariables']
            var['customerRegionName'] = getRegionName(
                var['customerRegionCode'])

        return tasks
Esempio n. 8
0
    def getOutstandingOrders(self):
        params = {
            'processDefinitionKey': 'worktop',
            'activityId': 'WorktopShipped',
            'unfinished': True
        }

        ret = cc.makeRequest(
            '/history/activity-instance',
            'post',
            params,
            urlParams={'maxResults': 100},
            withProcessVariables=('orderId', 'externalOrderId',
                                  'factoryNumber', 'customerName',
                                  'customerRegionCode',
                                  'scheduledInstallationDate',
                                  'productionDrawing', 'orderItems'),
            hoistProcessVariables=True)

        for p in ret:
            ois = p.get('orderItems')
            if ois:
                addItemInfo(ois)

            if 'customerRegionCode' in p:
                p['customerRegionName'] = getRegionName(
                    p['customerRegionCode'])
                del p['customerRegionCode']
        return ret
Esempio n. 9
0
    def __call__(self):
        assert 'orders' in self.request.params
        oids = self.request.params['orders'].split(',')
        assert oids

        labels = []
        for oid in oids:
            ret = cc.makeRequest('/process-instance',
                                 'post', {'businessKey': oid},
                                 withProcessVariables=(
                                     'orderId',
                                     'externalOrderId',
                                     'factoryNumber',
                                     'customerName',
                                     'customerRegionCode',
                                 ),
                                 processInstanceIdField='id',
                                 hoistProcessVariables=True)
            pkgId = ret[0]['pkgId'] = shortuuid.uuid()
            ret[0]['labelUrl'] = f'{self.request.host_url}/ikea/shipOrder?' \
                f'orderId={oid}&pkgId={pkgId}'
            labels.append(ret[0])

        loader = TemplateLoader([os.path.dirname(__file__)])
        template = loader.load('shippingLabel.rml')
        stream = template.generate(labels=labels, view=self)
        body = rml2pdf.parseString(stream.render()).read()

        response = Response(content_type='application/pdf',
                            content_disposition='filename="labels.pdf"',
                            content_length=len(body),
                            body=body)
        return response
Esempio n. 10
0
def add_variables():
    """
    For **active** process instances of the last version set the variable
    isInstallationRequested to 'true'.
    """
    # get a list of all active processIds, since we can not put variable on
    # already completed process instances
    processes = [p['id'] for p in cc.makeRequest('/process-instance', 'post', {
        'processDefinitionKey': 'worktop'
    })]

    for pid in processes:
        cc.makeRequest(
            f'/process-instance/{pid}/variables/isInstallationRequested', 'put',
            params={
                'value': 'true',
                'type': 'Boolean'
            }
        )
Esempio n. 11
0
    def changeTaskDueDate(self, taskId, dueDate, justification):
        """
        :param dueDate:
            a javascript object string, could be in UTC so careful
        """

        # to change the task due, we have to get the full task object and
        # modify the due, since the PUT method replace the task object as a
        # whole, not just one field
        task = cc.makeRequest(f'/task/{taskId}', 'get')
        newDue = parser.parse(dueDate).astimezone(tz.tzlocal()).replace(
            hour=23, minute=59, second=59)
        task['due'] = newDue.strftime('%Y-%m-%dT%H:%M:%S.0+0800')
        cc.makeRequest(f'/task/{taskId}', 'put', task)

        comment = '\n'.join(('任务期限从{old}修改为{new}'.format(
            old=parser.parse(task['due']).astimezone(
                tz.tzlocal()).strftime('%Y-%m-%d %H:%M:%S'),
            new=newDue.strftime('%Y-%m-%d %H:%M:%S')),
                             '修改原因: ' + justification))
        self.addTaskComment(taskId, comment)
Esempio n. 12
0
    def getProcessVariables(self, processInstanceId):
        variables = cc.parseVariables(
            cc.makeRequest(f'/history/variable-instance',
                           'post',
                           {'processInstanceIdIn': [processInstanceId]},
                           urlParams={'deserializeValues': 'false'}))

        ois = variables.get('orderItems')
        if ois:
            addItemInfo(ois)

        return variables
Esempio n. 13
0
    def __call__(self):
        key = self.request.matchdict['key']
        if key == 'current':
            today = date.today()
            self.startDate = today - timedelta(today.isocalendar()[2] + 6)
        else:
            assert len(key) == 6 and key.isnumeric()
            year, week = int(key[:4]), int(key[4:])
            assert week <= 52 and year >= 2018 and year <= 2050
            self.startDate = Week(year, week).monday()

        self.endDate = self.startDate + timedelta(6)
        cal = self.startDate.isocalendar()
        self.docKey = f'{cal[0]}/{cal[1]}'

        self.orders = self.sess.query(SalesOrder).\
            filter_by(orderSource=ORDER_SOURCE.IKEA).\
            filter_by(orderStatus=ORDER_STATUS.COMPLETED).\
            filter(SalesOrder.completionDate >= self.startDate).\
            filter(SalesOrder.completionDate < self.endDate + timedelta(1)).\
            all()
        self.total = sum([o.amount for o in self.orders])

        for o in self.orders:
            ret = cc.makeRequest(
                '/history/process-instance', 'post',
                {'processInstanceBusinessKey': o.orderId},
                withProcessVariables=('storeId', ),
                processInstanceIdField='id', hoistProcessVariables=True
            )[0]
            o.storeId = ret['storeId']

        self.orders.sort(key=lambda o: (o.storeId, o.orderId))

        loader = TemplateLoader([os.path.dirname(__file__)])
        template = loader.load('receivable.pt')
        stream = template.generate(view=self)
        body = rml2pdf.parseString(stream.render()).read()

        response = Response(
            content_type='application/pdf',
            content_disposition=f'filename="AR{self.docKey}.pdf"',
            content_length=len(body),
            body=body)
        return response
Esempio n. 14
0
    def receiveWorktop(self, orderId, pkgId):
        """
        For orders with multpile packages, the method will record all received
        packages in the process variable `receivedPackages`

        :param orderId:
            The ERP order id
        :param pkgId:
            Package id as generated by shortuuid
        """

        # first check the order is still active
        process = cc.makeRequest(
            '/process-instance',
            'post',
            params={'businessKey': orderId},
            withProcessVariables=('receivedPackages', 'externalOrderId',
                                  'customerName', 'customerMobile',
                                  'customerRegionCode'),
            processInstanceIdField='id')

        # this could be the scanning of an old label for a process already
        # completed
        if len(process) != 1:
            raise RPCUserError(f'订单{orderId}不在待收货状态')

        process = process[0]
        # the already received packages
        pkgs = process['processVariables'].get('receivedPackages', [])
        if pkgId in pkgs:
            raise RPCUserError('该件已收货,请勿重复收货')

        # now check if the process is waiting for the receive signal. If
        # there is any packages already received for the order, it will be no
        # longer waiting for the receive signal
        exe = cc.makeRequest('/execution',
                             'post',
                             params={
                                 'signalEventSubscriptionName':
                                 'WorktopReceived',
                                 'processDefinitionKey': 'worktop',
                                 'businessKey': orderId
                             })

        if not exe and not pkgs:
            raise RPCUserError(f'订单{orderId}不在待收货状态')

        # this should not be valid combination
        if exe and pkgs:
            raise RPCUserError(f'系统错误')

        if len(exe) == 1:
            cc.makeRequest('/signal',
                           'post',
                           params={
                               'name': 'WorktopReceived',
                               'executionId': exe[0]['id']
                           })

        # update the process's receivedPackages variable
        pkgs.append(pkgId)
        cc.makeRequest(
            f'/process-instance/{process["id"]}'
            '/variables/receivedPackages',
            'put',
            params=cc.convertVariables({'var': pkgs})['var'])

        # return the order header for confirmation
        return process['processVariables']
Esempio n. 15
0
def searchProcess(cond, request, countOnly=False, maxRows=50):
    """ Note the startDate and endDate will be passed in UTC """
    cond['storeId'] = request.user.extraData['worktop'].get('storeId')

    params = {
        'processDefinitionKey': 'worktop',
        'sorting': [{
            'sortBy': 'startTime',
            'sortOrder': 'desc'
        }]
    }

    searchText = cond.get('searchText')
    showCompleted = cond.get('completed')

    if searchText:
        if isOrderId(searchText):
            params['processInstanceBusinessKey'] = searchText
        elif isIkeaOrderId(searchText):
            params['variables'] = [{
                'name': 'externalOrderId',
                'operator': 'eq',
                'value': searchText.upper()
            }]
        elif isMobile(searchText):
            params['variables'] = [{
                'name': 'customerMobile',
                'operator': 'eq',
                'value': searchText
            }]
        else:
            params['variables'] = [{
                'name': 'customerName',
                'operator': 'eq',
                'value': searchText
            }]
    else:
        parseDate(cond, fields=['startDate', 'endDate'])
        startDate, endDate = cond['startDate'], cond['endDate']
        endDate = endDate + timedelta(1)

        if (endDate - startDate) > timedelta(365):
            raise RPCUserError('订单查询时间跨度不能大于1年。')

        if showCompleted:
            params['variables'] = [{
                'name': 'actualInstallationDate',
                'operator': 'gteq',
                'value': startDate
            }, {
                'name': 'actualInstallationDate',
                'operator': 'lt',
                'value': endDate
            }]
        else:
            params['startedBefore'] = endDate
            params['startedAfter'] = startDate

    storeId = cond['storeId']
    if storeId:
        variables = params.setdefault('variables', [])
        variables.append({
            'name': 'storeId',
            'operator': 'eq',
            'value': storeId
        })

    if countOnly:
        ret = cc.makeRequest(
            '/history/process-instance/count',
            'post',
            params,
        )
        if ret['count'] > 500:
            raise RPCUserError('单次导出结果大于500条,请搜索条件再')
        return ret['count']
    else:
        ret = cc.makeRequest(
            '/history/process-instance',
            'post',
            params,
            urlParams={'maxResults': maxRows},
            withProcessVariables=('externalOrderId', 'customerName', 'storeId',
                                  'orderItems', 'receivingDate',
                                  'isInstallationRequested',
                                  'actualMeasurementDate',
                                  'confirmedMeasurementDate',
                                  'scheduledMeasurementDate',
                                  'actualInstallationDate',
                                  'confirmedInstallationDate',
                                  'scheduledInstallationDate'),
            processInstanceIdField='id',
            hoistProcessVariables=True)

        #
        # Prepare for display by adding additional infos:
        #  * add model to orderItems as only itemId is stored
        #  * add human readable status text
        #
        currentTime = datetime.now()
        for p in ret:
            # The instance variables of Date type are parsed correctly, but the
            # process property is not. We will do the parse here.
            parseDate(p, fields=['startTime'])
            p['startTime'] = p['startTime'].astimezone(tzLocal).replace(
                tzinfo=None)

            # calculate the duration of the process.
            if 'TERMINATED' not in p['state']:
                start = p.get('actualMeasurementDate') or \
                    p.get('scheduledMeasurementDate') or p['startTime']
                end = p.get('actualInstallationDate') or currentTime
                delta = end - start
                if delta.total_seconds() > 0:
                    p['duration'] = delta.days + decimal_round(
                        Decimal(delta.seconds / 86400), Decimal('0.1'))

            state = p.pop('state')
            if state == 'ACTIVE':
                if p.get('actualInstallationDate'):
                    text = '已安装' \
                        if p.get('isInstallationRequested', True) else '已送货'
                elif p.get('confirmedInstallationDate'):
                    text = '待安装'
                elif p.get('receivingDate'):
                    text = '已收货'
                elif p.get('actualMeasurementDate') or \
                        not p.get('scheduledMeasurementDate'):
                    text = '生产中'
                elif p.get('confirmedMeasurementDate') or \
                        p.get('scheduledMeasurementDate'):
                    text = '待测量'
                else:
                    text = '进行中'
                p['statusText'] = text
            else:
                p['statusText'] = __StatusNames__[state]

            ois = p.get('orderItems')
            if ois:
                addItemInfo(ois)

        return ret
Esempio n. 16
0
    def startProcess(self, params):
        params = parseDate(
            params,
            fields=['scheduledMeasurementDate', 'scheduledInstallationDate'])

        if not params['scheduledInstallationDate']:
            params.pop('scheduledInstallationDate')

        if not params['isMeasurementRequested']:
            params.pop('scheduledMeasurementDate', None)
            params['productionDrawing'] = params['orderFile']

        externalOrderId = params.get('externalOrderId')
        if externalOrderId and \
                self.sess.query(SalesOrder).filter_by(
                    orderSource=ORDER_SOURCE.IKEA,
                    externalOrderId=externalOrderId
                ).all():
            raise RPCUserError('该订单号已存在,不能重复提交')

        order = SalesOrder(customerId=int(SPECIAL_PARTY.BE),
                           creatorId=self.request.user.partyId,
                           regionCode=params['customerRegionCode'],
                           streetAddress=params['customerStreet'],
                           recipientName=params['customerName'],
                           recipientMobile=params['customerMobile'],
                           orderSource=ORDER_SOURCE.IKEA)

        if params['isMeasurementRequested']:
            itemMeasure = self.sess.query(Item).get(10023928)
            order.addItem(item=itemMeasure, quantity=1)

        serviceItem = self.sess.query(Item).get(
            10023929 if params['isInstallationRequested'] else 10024028)
        order.addItem(item=serviceItem, quantity=1)

        self.sess.add(order)
        self.sess.flush()

        po = PurchaseOrder(supplierId=10025188,
                           customerId=int(SPECIAL_PARTY.BE),
                           creatorId=self.request.user.partyId,
                           regionCode=params['customerRegionCode'],
                           streetAddress=params['customerStreet'],
                           recipientName=params['customerName'],
                           recipientMobile=params['customerMobile'],
                           relatedOrderId=order.orderId)
        if params['isMeasurementRequested']:
            po.addItem(item=itemMeasure, quantity=1)
        po.addItem(item=serviceItem, quantity=1)
        self.sess.add(po)

        if externalOrderId:
            order.externalOrderId = externalOrderId
        else:
            params['externalOrderId'] = str(order.orderId)

        # add orderId also as a process instance variable
        params['orderId'] = order.orderId

        cc.makeRequest(f'/process-definition/key/worktop/start',
                       'post', {'businessKey': order.orderId},
                       variables=params)
Esempio n. 17
0
 def addTaskComment(self, taskId, comment):
     cc.makeRequest(f'/task/{taskId}/comment/create', 'post',
                    {'message': comment})
Esempio n. 18
0
    def shipWorktop(self, extOrderIds):
        """
        Send the WorktopShipped signal to all waiting processes given by the
        orderId list.

        :param orderIds:
            A list of externalOrderId
        :raises RPCUserError:
            Raises RPCUserError if any order can not be shipped or if any error
            occurs during the signal sending to any order.
        """
        errors = []
        executions = []
        extOrderIds = set(extOrderIds)  # deduplicate

        for externalOrderId in extOrderIds:
            ret = cc.makeRequest(
                '/execution',
                'post',
                params={
                    'signalEventSubscriptionName':
                    'WorktopShipped',
                    'processDefinitionKey':
                    'worktop',
                    'processVariables': [{
                        'name': 'externalOrderId',
                        'operator': 'eq',
                        'value': externalOrderId
                    }]
                },
                withProcessVariables=('externalOrderId',
                                      'scheduledInstallationDate'),
                hoistProcessVariables=True)

            if not ret:
                errors.append(f'未找到待发货的订单{externalOrderId}')
            elif len(ret) != 1:
                errors.append(f'订单{externalOrderId}无法发货发货')
            else:
                executions.append(ret[0])

        if errors:
            raise RPCUserError('\n'.join(errors))

        for exe in executions:
            try:
                # TODO: this is a temporary fix so that process without
                # scheduledInstallationDate can continue past into next step by
                # defaulting the date to 3 days from shipment date
                variables = cc.convertVariables(
                    {'scheduledInstallationDate': date.today() + timedelta(3)})
                cc.makeRequest(
                    f'/process-instance/{exe["processInstanceId"]}'
                    '/variables/scheduledInstallationDate',
                    'put',
                    params=variables['scheduledInstallationDate'])

                cc.makeRequest('/signal',
                               'post',
                               params={
                                   'name': 'WorktopShipped',
                                   'executionId': exe['id']
                               })
            except CamundaRESTError:
                errors.append(f"订单{exe['externalOrderId']}发货错误")

        if errors:
            raise RPCUserError('\n'.join(errors))