Ejemplo n.º 1
0
    def addFileObject(self, data):
        """
        Upload the file object to OSS. For now we only accept images.

        - `data`: Content of the data to add as base64 encoded binary

        Returns the name of the uploaded object.
        """
        data = b64decode(data)
        md5 = hashlib.md5(data).hexdigest()

        # first check duplication on the original file
        fname = self.getFileObjectByMd5(md5)
        if fname:
            return fname

        try:
            img = PIL.Image.open(io.BytesIO(data))
        except OSError:
            raise RPCUserError('该文件不是图片格式。')

        if img.format not in ('JPEG', 'PNG', 'GIF'):
            raise RPCUserError('只支持jpg、png、gif格式的图片。')

        # images uploaded via `fileobject.add` shall already be optimized on
        # the client side and hence should not be optimized again
        return self.addImage(img, optimize=False, data=data)
Ejemplo n.º 2
0
    def setBom(self, item, bomItems):
        """ Update the boms attribute of an item.

        The `bomItems` argument is a **list** of [item_id, quantity]
        which describes completely how the item is composed of. Set it to
        an empty list to remove bom if any were present.
        """
        assert isinstance(bomItems, list), 'bomItems must be a list'

        if len(bomItems) == 1 and bomItems[0][1] == 1:
            raise RPCUserError('部件清单不能只有一个数量为1的项目。')

        assert item.itemId not in [i[0] for i in bomItems], \
            'Component item can not be the item itself'

        # check if item is already a component of another
        bi = self.sess.query(BomItem).filter(
            BomItem.componentItemId == item.itemId).first()
        if bi:
            raise RPCUserError('该商品已经是商品%d的部件。' % bi.itemId)

        weight = 0
        for (iid, quantity) in bomItems:
            ci = self.sess.query(Item).get(iid)
            assert ci.isSku, 'Item %d is not a sku' % iid
            assert quantity, 'Quantity can not be 0'
            if not ci.weight:
                # =============================================================
                # once set to None, weight remains None, since it
                # means some component has unknown weight
                # =============================================================
                weight = None
            elif weight is not None:
                weight += ci.weight * quantity

        # remove all exisitng bom first if any is there
        if not item.isSku:
            item.boms.clear()
            item.isSku = True

        # now set the new bom
        for (idx, (iid, quantity)) in enumerate(bomItems):
            item.boms.append(
                BomItem(itemId=item.itemId,
                        componentItemId=iid,
                        quantity=quantity,
                        componentOrder=idx))
            item.isSku = False
        item.weight = weight
Ejemplo n.º 3
0
    def payPurchaseOrder(self, orderId):
        sess = self.sess
        order = sess.query(PurchaseOrder).get(orderId)

        assert order, RPCUserError('订单号错误')
        assert order.orderStatus == ORDER_STATUS.COMPLETED, \
            RPCUserError('订单未完成,不能付款')

        openId = order.supplier.wechatOpenId
        assert openId, RPCUserError('该供应商尚未关联微信')

        amount = order.payableAmount
        assert amount, RPCUserError('订单没有未付金额')

        params = {
            'partner_trade_no': '%dT%d' % (order.orderId, time.time()),
            'openid': openId,
            'check_name': 'NO_CHECK',
            'amount': int(amount * 100),
            'desc': '大管家订单%d' % order.orderId,
            # this parameter is required but the value is not important
            'spbill_create_ip': '192.168.0.1'
        }
        ret = wc.makeRequest('mmpaymkttransfers.promotion.transfers',
                             params,
                             cert=True)

        if ret.result_code == 'FAIL':
            raise RPCUserError(ret.err_code_des)

        payment = Payment(amount=amount,
                          externalPaymentId=str(ret.payment_no),
                          externalUserId=openId,
                          payTime=datetime.strptime(str(ret.payment_time),
                                                    '%Y-%m-%d %H:%M:%S'),
                          partyId=order.supplierId,
                          outTradeNo=str(ret.partner_trade_no),
                          creatorId=self.request.user.partyId,
                          paymentGateway=PAYMENT_GATEWAY.WEPAY,
                          paymentMethod=27)
        sess.add(payment)
        sess.flush()

        sess.add(
            OrderPayment(orderId=order.orderId,
                         paymentId=payment.paymentId,
                         amount=amount))
        order.paidAmount += amount
Ejemplo n.º 4
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
Ejemplo n.º 5
0
    def setImages(self, item, imageIds):
        """ Update the item image association. Note that for simplicity we use
        this function for all the add, insert, move, delete of item images.
        Just pass in a list of images that should be the correct one and we
        will set it.

        Note this function has nothing to do images used by the item's
        description modules"""

        # check that the main item image is square
        for iid in imageIds:
            image = self.sess.query(Image).get(iid)
            if abs(image.width - image.height) > 1:
                raise RPCUserError('商品主图必须是正方形。')

        if item.images:  # this is necessary to load images first
            item.images.clear()

        if imageIds:
            for (idx, iid) in enumerate(imageIds):
                item.images.append(
                    ItemImage(itemId=item.itemId, imageId=iid, imageOrder=idx))
            mainImage = self.sess.query(Image).get(imageIds[0])
            item.mainImageFile = '%s.%s' % \
                (mainImage.imageId, mainImage.format)
        else:
            item.mainImageFile = None
Ejemplo n.º 6
0
    def setItemStatus(self, item, status):
        # check existence of available image
        if status == ITEM_STATUS.ONLINE:
            if item.primaryCategoryId == 1810:
                raise RPCUserError('待审核商品%d不能上线!' % item.itemId)

            if not (item.sellingPrice and item.sellingPriceB):
                raise RPCUserError('不能上线无售价商品!')

            if not item.mainImageFile and (
                    not item.itemGroup or not item.itemGroup.shareImage
                    or not self.sess.query(Item).get(
                        item.itemGroup.items[0]).mainImageFile):
                raise RPCUserError('商品%d没有主图不能上线!' % item.itemId)

        item.itemStatus = status
Ejemplo n.º 7
0
    def handleCompleteERPOrder(self, taskId, variables):  # pylint: disable=W0613
        orderId = variables['orderId']
        so = self.sess.query(SalesOrder).get(orderId)

        if not so:
            raise RPCUserError(f'未找到ERP订单{orderId}')

        if so.orderStatus != ORDER_STATUS.COMPLETED:
            raise RPCUserError(f'销售订单{orderId}未完成')

        query = self.sess.query(PurchaseOrder).\
            filter_by(relatedOrderId=orderId)
        orders = query.all()

        if not orders or \
                [o for o in orders if o.orderStatus != ORDER_STATUS.COMPLETED]:
            raise RPCUserError(f'未找到该订单对应的采购订单或采购订单未完成')
Ejemplo n.º 8
0
    def searchSku(self, text):
        """ Search sku with the given text. Used only by the `skuinput` widget

        If text is 8-digits number, it will be interpreted first as item id.
        Then we will search for items with text as the exact model_no.
        If there is an exact match, that match will be returned.
        If not found, we will search items with text as starting model_no.
        If still not found, search for items whose model_no or item_name
        contains the given text.

        Note that inactive items will not be returned. """

        ret = set()
        query = self.sess.query(Item).filter(
            and_(Item.itemStatus != ITEM_STATUS.INACTIVE, Item.isSku == True))

        if len(text) < 3:
            raise RPCUserError('商品型号最少长度为3个字符。')

        # find an exact match for model. Could be multiple items
        items = query.filter(func.upper(Item.model) == text.upper()).all()

        # if nothing found yet, match starting string of model
        if not items:
            items = query.filter(
                func.upper(func.substr(Item.model, 1, len(text))) ==
                text.upper()).limit(11).all()

        if not items:
            items = query.filter(
                or_(
                    Item.itemName.op('ilike')('%%%s%%' % text),
                    Item.specification.op('ilike')('%%%s%%' % text),
                    Item.model.op('ilike')('%%%s%%' % text))).limit(11).all()

        if len(items) > 10:
            return []

        if items:
            for i in items:
                ret.add(i)

        if Item.IsItemNumber(text):
            item = self.sess.query(Item).get(text)
            if item:
                ret.add(item)

        return [{
            'itemId': i.itemId,
            'itemName': i.itemName,
            'model': i.model,
            'specification': i.specification,
            'unitId': i.unitId,
            'weight': i.weight
        } for i in ret]
Ejemplo n.º 9
0
    def save(self, kwargs):
        """
        Creates a new or updates an existing article.

        TODO:
        The elasticsearch _id field is used to locate the article instead of
        the articleId field. It's confusing to use two different id for one
        object. A reindex should be able to migrate existing documents to
        using one articleId. Also the createDate field in the mapping is not
        used and shall be removed in a reindex.

        When creating a new article, fetch a sequence id from postgresql as its
        articleId.
        """
        _id = kwargs.pop('_id')
        is_new = _id.startswith('Web.model')  # is ths a new article?
        kwargs.pop('updateTime', None)  # this shall be automatically set
        tags = kwargs.pop('tags', None)

        content = kwargs.get('content')
        # checks the content for external image urls
        if content:
            soup = BeautifulSoup(content, 'lxml')
            for img in soup.select('img'):
                host = urlparse(img['src']).netloc
                if not host.endswith('homemaster.cn'):
                    raise RPCUserError('文章中不能包含外部图片链接!')

        if is_new:
            if kwargs['articleType'] == 'case':
                kwargs.pop('title')

            article = Article(**kwargs)
            article.updateTime = datetime.now()
        else:
            article = Article.get(_id)
            # update the article update time only when content is changed
            if article.content != kwargs.get('content'):
                article.updateTime = datetime.now()
            for (k, v) in kwargs.items():
                setattr(article, k, v)

        if tags:
            article.tags = [tag.strip() for tag in tags.split(',')]
        # tags is explicitly cleared
        elif tags == '' and hasattr(article, 'tags'):
            del article.tags

        # generate an numberic article id for new articles
        if is_new:
            article.articleId = self.sess.execute(Sequence('resource_id_seq'))

        article.save()
        return self.getData(article)
Ejemplo n.º 10
0
    def updateContent(self, ig, content):
        for f in ('groupBy', 'shareDescription', 'shareImage', 'groupItemName',
                  'groupImageId'):
            setattr(ig, f, content.get(f))

        # check if any item is already a member of other group
        iids = [i['itemId'] for i in content['items']]
        items = self.sess.query(Item).filter(
            and_(Item.itemId.in_(iids),
                 Item.itemGroupId != ig.itemGroupId)).all()
        if items:
            raise RPCUserError('商品%d已定义商品组合' % items[0].itemId)

        firstItem = self.sess.query(Item).get(iids[0])

        if ig.shareImage and not firstItem.mainImageUrl:
            raise RPCUserError('商品组合第一个商品没有主图')

        if ig.shareDescription and not firstItem.descriptionModules:
            raise RPCUserError('商品组合第一个商品没有描述')

        if ig.groupImageId and not \
            self.sess.query(ItemImage).filter(
                ItemImage.imageId == ig.groupImageId).all():
            raise RPCUserError('图片id不存在或不是商品的主图')

        if ig.groupBy == 'L':
            if not content['labelName'].strip():
                raise RPCUserError('标签名称不能为空')

            ig.groupCriteria = {
                'labelName': content['labelName'],
                'labels': [i['label'] for i in content['items']],
                'thumbs': [i['thumb'] for i in content['items']]
            }

            ig.items = [i['itemId'] for i in content['items']]
Ejemplo n.º 11
0
def userLogin(request, login, password, appName):
    """
    This and 'auth.logout' are the only rpc methods that does not subclass
    RpcBase for they must be invoked without valid user session.
    """
    sess = DBSession()
    user = sess.query(Party).filter_by(login=login).first()

    if not user or not user.verifyPassword(password):
        raise RPCUserError('登录失败,请检查用户名和密码!')

    # Only those with defined permission are allowed
    if not user.extraData or appName not in user.extraData:
        raise RPCNotAllowedError('您无权登录当前应用。')

    # after user is authenticated, we cache the user object in redis
    sess.expunge_all()  # detach from session
    request.session['user'] = user

    # copy so that we do not persist the token in the session user object
    ret = user.extraData[appName].copy()
    ret['csrfToken'] = get_csrf_token(request)
    return ret
Ejemplo n.º 12
0
    def checkAndConvertImage(self, data, fname, imgType):
        """
        Helper function that returns a **PIL.Image** object and the binary
        image data of subject to the following transformations:

        *  if the images is in PSD format, convert it to PNG and extract text
           content from the file

        *  for item image, image are sized down to 800x800 or 1200x1200
           depending on the original size. If the input image is not square,
           it will be centered appropriately vertically or horizontally.

        *  for description image, if the image width exceeds 790, it is resized
           to 790 while keeping the image aspect ratio
        """
        data = b64decode(data)

        if fname.endswith('.psd'):
            psd = PSDImage.from_stream(io.BytesIO(data))
            content = extractTextFromPSD(psd)
            img = psd.as_PIL()
        else:
            try:
                img = PIL.Image.open(io.BytesIO(data))
            except OSError:
                raise RPCUserError('该文件不是图片格式。')

            if img.format not in ('JPEG', 'PNG', 'GIF'):
                raise RPCUserError('只支持jpg、png、gif格式的图片。')
            content = None

        modified = False
        if imgType == 'item':
            if img.width < 800 and img.height < 800:
                raise RPCUserError('商品主图宽度和高度必须至少有一个超过800。')
            if len(data) > 500 * 1024:
                raise RPCUserError('商品主图大小不能超过500KB。')

            out_size = 1200 if img.width >= 1200 or img.height >= 1200 else 800
            ratio = min(out_size / img.width, out_size / img.height)

            w, h = int(img.width * ratio), int(img.height * ratio)
            if w != out_size or h != out_size:
                img = img.resize((w, h), PIL.Image.LANCZOS)
                out_img = PIL.Image.new('RGB', (out_size, out_size), 'white')
                out_img.paste(img, ((out_size - w) // 2, (out_size - h) // 2))
                modified = True
                img = out_img

        else:  # this is image for description
            if img.width < 790:
                raise RPCUserError('商品描述图片宽度最小为790。')
            if not 0.3 <= img.height / img.width <= 2:
                raise RPCUserError('商品描述图片高宽比(高度/宽度)必须在0.3-2之间。')
            if img.width > 790:
                img = img.resize((790, int(790 / img.width * img.height)),
                                 PIL.Image.LANCZOS)
                modified = True

        if modified:
            img.format = 'JPEG'
            stream = io.BytesIO()
            img.save(stream, img.format, optimize=True, quality=95)
            data = stream.getvalue()

        if len(data) > 2 * 1024 * 1024:
            raise RPCUserError('图片大小不能超过2MB。')

        return img, data, content
Ejemplo n.º 13
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
Ejemplo n.º 14
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)
Ejemplo n.º 15
0
    def searchItem(self,
                   text=None,
                   brandId=None,
                   catId=None,
                   status=None,
                   isSku=False,
                   assortmentOnly=False):
        """
        Search items based on given conditions. When status is not given,
        no inactive items will be returned. To search for items of all status,
        set status to 'all'.

        Returns a list or found Items. If not found, an empty list is returned.
        """
        query = self.sess.query(Item)

        if not (catId or text or brandId):
            raise RPCUserError('请指定搜索条件!')

        if catId:
            cs = categoryFactory()
            node = cs.get(int(catId))
            query = query.filter(
                Item.primaryCategoryId.in_([n.key for n in node.leafNodes]))

        if text:
            or_clause = [
                Item.itemName.op('ilike')('%%%s%%' % text),
                Item.specification.op('ilike')('%%%s%%' % text),
                Item.model.op('ilike')('%%%s%%' % text)
            ]
            query = query.filter(or_(*or_clause))

        if brandId:
            query = query.filter_by(brandId=brandId)

        if isSku:
            query = query.filter_by(isSku=isSku)

        if assortmentOnly:
            cs = categoryFactory()
            query = query.filter(
                not_(
                    Item.primaryCategoryId.in_(
                        [n.key for n in cs.get(1800).leafNodes])))

        if status is None:
            query = query.filter(Item.itemStatus != ITEM_STATUS.INACTIVE)
        elif isinstance(status, int):
            query = query.filter_by(itemStatus=status)
        elif status == 'all':
            pass
        else:
            raise ValueError('Invalid status parameter')

        items = query.all()

        # if the search text could be an item number, we append it to the
        # search result.
        if text and Item.IsItemNumber(text):
            item = self.sess.query(Item).get(int(text))
            if item:
                items.append(item)

        return self.marshall(items)
Ejemplo n.º 16
0
    def download(self, itemId, itemUrl):
        host = urlparse(itemUrl).netloc
        if host == 'item.taobao.com':
            raise RPCUserError('不支持从集市店铺导入商品描述。')
        elif host != 'detail.tmall.com':
            raise RPCUserError('商品链接不正确。')

        item = self.sess.query(Item).get(itemId)
        assert item, 'Invalid itemId'

        ret = requests.get(itemUrl)
        if not ret.ok:
            raise RPCUserError('链接访问失败,请确定商品链接是否正确。')

        itemInfo = parseTmall(ret.text)
        #
        # Processing item image
        #
        imageIds = []
        for imgurl in itemInfo['images']:
            image = None
            ret = requests.get(imgurl)
            data = ret.content
            if not ret.ok or ret.headers['Content-Type'] not in \
                    ('image/jpeg', 'image/png', 'image/gif'):
                continue

            image = self.findImage(data)
            if not image:
                img = PIL.Image.open(io.BytesIO(data))
                if img.width < 600 and img.height < 600:
                    continue
                image = self._uploadImage(img, data)
            imageIds.append(image.imageId)

        if imageIds:
            self.setImages(item, imageIds)

        modules = {}
        for (mid, mimages) in itemInfo['modules'].items():
            if mid not in __desc_module_order__:
                continue

            imageIds = []
            for imgUrl in mimages:
                image = None
                ret = requests.get(imgUrl)
                data = ret.content
                if not ret.ok or ret.headers['Content-Type'] not in \
                        ('image/jpeg', 'image/png', 'image/gif'):
                    continue

                image = self.findImage(data)
                if not image:
                    img = PIL.Image.open(io.BytesIO(data))
                    if img.width < 750 or img.height / img.width < 0.3:
                        continue
                    if img.width != 790:
                        fmt = img.format  # after resize, format would be lost
                        img = img.resize(
                            (790, int(790 / img.width * img.height)),
                            PIL.Image.LANCZOS)
                        img.format = fmt
                        stream = io.BytesIO()
                        img.save(stream, img.format)
                        data = stream.getvalue()
                        image = self.findImage(data)
                        if not image:
                            image = self._uploadImage(img, data)
                    else:
                        image = self._uploadImage(img, data)

                imageIds.append(image.imageId)

            if imageIds:
                modules[mid] = imageIds

        if modules:
            item.descriptionModules = modules
Ejemplo n.º 17
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']
Ejemplo 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))