Mixoo

Hi, 好久不见


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

案例分享——北京某漫画行业应用案例

发表于 2020-09-02 | 分类于 odoo

首先恭喜在忙碌了一个半月之后,客户北京某动漫行业初创公司二期项目顺利毕业。本文主要回顾和总结一下客户需求及实现的思路和方案,以供广大读者参考。

客户需求

客户M是互联网平台在线动漫的供应商,主要给腾讯动漫等平台供稿,创作人员多为外包形式,因此项目的组织进度、绩效考核和费用结算就成为了HR每月底的重头戏。客户希望可以借助Odoo解决这些痛点,实现项目任务的在线分发、自动流转同时可以对各个阶段的人员的工作KPI进行考核、以及月底的薪酬的统计和发放。

整个项目分为多个部分,阶段主要是包含动漫产业流程的各个阶段,每个人负责一个阶段,一个阶段在完成以后要自动流转到下一个阶段的负责人那里,实现自动通知。计价系统负责对每个任务的价格核定,并根据任务的完成情况对考核人员进行评级,最终得出一个应付的价格。考核系统就是根据任务的完成情况、整体的流畅度等因素得出一个得分。结算系统负责每个月对所有人员当月内完成的任务进行统计,实现薪酬的自动计算,节省人工核算成本。HR系统主要是对人员的管理,在线签约、入/离职等流程管理等功能。

项目亮点

多平台管理

客户拥有多个发布平台,因此必须要在项目的基础上再抽象出来一个平台概念。同时由于每个平台的业务不同,类型不同、定价单位和定价方式,考核标准也不尽相同,要做到灵活可变,因此给平台新增了很多特性:

根据每个平台的特点,用户可以引入不同的项目阶段,并指定不同的考核标准和定价。

考核自动评价

人员在接到任务以后,系统会自动发出邮件通知。并会在任务结束时根据完成时间确定该任务是否准时完成,并将延期情况计入评分系统。

任务完成以后,由考核人员对该阶段任务做出考核结果评价,根据评价,自动计算出该任务的得分和应得薪酬。

自动结算

每个月末,系统管理员可以根据上个月的业务情况,安排自动运行项目结算任务,系统将自动根据每个人员的考核结果自动计算出应得的薪酬。

项目难点

根据整体需求,使用了odoo自动的项目管理功能,并在它的基础上进行了扩展。整个项目的难点在于,客户的平台有多个,设计的业务也有多种类型,每个作品的阶段以及定价方式、考核标注各不相同。考虑到以后的拓展性,不能将各个参数固化到代码中,因此必须以一种灵活可变的方式将各个平台的业务统一起来。

这里采用了动态编码的方式,即将项目的动态因素以规则的方式放到设置中,而此规则可以在系统中以伪代码的形式存储,客户可以根据自己的需求动态调整计算逻辑,而无需开发人员介入。

说的比较抽象,这里我们简单举一个例子:

某平台T有一个作品P,P有N个阶段: 给稿、翻译、校对、制作、监制、交稿等阶段,我们要实现不同阶段的任务评分不一样,考核标注也不同。

我们这里拓展了阶段,加入了考核标准,每个考核标准的量化参数都可以自定义,每个量化参数的计算规则都可以在线编辑,以伪代码的方式参与到计算中。

这里的伪代码,实际上是通过Python的eval函数实现的,目的主要就是为了避免因为后期考核/计价逻辑的改变而需要重构代码,只要客户学会了简单的Python脚本,就可以实现计算逻辑的动态可变。

问题总结

由于客户并非专业产品人员,对Odoo也并非特别熟悉,因此整个项目的推进没有预期的那么顺利,很多东西都是在做的时候才发现不能做,因此耽误了很多工期。项目开始之前,客户只是有一个模糊的大概的东西,具体会做成什么样子其实也没底,因此也不能一直停留在沟通阶段,很多时候,客户并不清楚自己想要的是什么,这个时候你就给他一个方案,他在看了你的方案之后会知道是不是自己想要的,然后我们再在这个基础上进行调整和优化。

Odoo中的路线规则浅析

发表于 2020-08-05 | 分类于 odoo

本文假设读者已经了解odoo路线的推拉规则

路线规则的优先级

我们知道,odoo中的路线可以在四个地方设置,分别是产品路线、产品分类路线、仓库路线和订单明细行中的路线。这四种路线的优先级为:

1
销售订单明细行 ——> 产品路线——> 产品分类路线——> 仓库路线

这个很好理解,当几个路线对某个库位设置的规则冲突时,依据本优先级进行应用。现在问题来了,我们知道产品路线中有多个路线可以选择,当产品路线中的多个路线的多个规则定义了相同的库位的规则时,会怎样呢?

隐藏的规则

相同级别的路线如果定义了相同的库位规则,官方并没有给出文档解释,我们从代码中可以窥见这隐秘的规则:

我们知道在早期的版本中,驱动销售产生采购、生产单的背后的大佬是需求单(procurement.order),到了13.0中已经演变成了procurement.group,需求单也不复存在,而且这家伙并不为普罗大众所熟知,造成的结果就是,很容易陷在路线的泥潭里而百思不得其解。

我们先来看procurement.group中是如何运行路线规则的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@api.model
def run(self, procurements):
""" Method used in a procurement case. The purpose is to supply the
product passed as argument in the location also given as an argument.
In order to be able to find a suitable location that provide the product
it will search among stock.rule.
"""
actions_to_run = defaultdict(list)
errors = []
for procurement in procurements:
procurement.values.setdefault('company_id', self.env.company)
procurement.values.setdefault('priority', '1')
procurement.values.setdefault('date_planned', fields.Datetime.now())
if (
procurement.product_id.type not in ('consu', 'product') or
float_is_zero(procurement.product_qty, precision_rounding=procurement.product_uom.rounding)
):
continue
rule = self._get_rule(procurement.product_id, procurement.location_id, procurement.values)
if not rule:
errors.append(_('No rule has been found to replenish "%s" in "%s".\nVerify the routes configuration on the product.') %
(procurement.product_id.display_name, procurement.location_id.display_name))
else:
action = 'pull' if rule.action == 'pull_push' else rule.action
actions_to_run[action].append((procurement, rule))

...

由此可见,关键方法_get_rule负责根据需求寻找对应的路线规则。所以,我们来看一下 _get_rule的具体内容:

1
2
3
4
5
6
7
8
9
10
11
12
@api.model
def _get_rule(self, product_id, location_id, values):
""" Find a pull rule for the location_id, fallback on the parent
locations if it could not be found.
"""
result = False
location = location_id
while (not result) and location:
domain = self._get_rule_domain(location, values)
result = self._search_rule(values.get('route_ids', False), product_id, values.get('warehouse_id', False), domain)
location = location.location_id
return result

这里有两个方法,_get_rule_domain负责查找库位的domain,_search_rule负责根据传入的库位查找规则。_get_rule_domain的功能比较简单,就是在规则中查找源库位为location_id且不是推式物流的规则。而_search_rule才是真正查找路线规则的关键先生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@api.model
def _search_rule(self, route_ids, product_id, warehouse_id, domain):
""" First find a rule among the ones defined on the procurement
group, then try on the routes defined for the product, finally fallback
on the default behavior
"""
if warehouse_id:
domain = expression.AND([['|', ('warehouse_id', '=', warehouse_id.id), ('warehouse_id', '=', False)], domain])
Rule = self.env['stock.rule']
res = self.env['stock.rule']
if route_ids:
res = Rule.search(expression.AND([[('route_id', 'in', route_ids.ids)], domain]), order='route_sequence, sequence', limit=1)
if not res:
product_routes = product_id.route_ids | product_id.categ_id.total_route_ids
if product_routes:
res = Rule.search(expression.AND([[('route_id', 'in', product_routes.ids)], domain]), order='route_sequence, sequence', limit=1)
if not res and warehouse_id:
warehouse_routes = warehouse_id.route_ids
if warehouse_routes:
res = Rule.search(expression.AND([[('route_id', 'in', warehouse_routes.ids)], domain]), order='route_sequence, sequence', limit=1)
return res

这个方法印证了我们之前的结论:

  1. odoo会先查找给定的路线,如果没有指定路线,则在产品路线或者产品分类路线中查找,如果没有则在仓库的路线中查找。
  2. 相同优先级的路线规则,则依据规则的route_sequence和sequene进行排序,取第一个规则。

实际中的例子

其实关于odoo路线的讨论文章已经不少了,今天之所以会重新总结一遍,是因为在实际使用过程中又一次在这里摔了跟头。简单回顾一下问题发生的过程:

  1. 首先,odoo中制造的路线,默认的生产库位为WH/Stock。
  2. 因为某种原因,我们把Stock改成了视图库位,并在其下设置了很多内部库位。
  3. 销售某产品,产品路线设置MTO和Manufacture。生成的生产单无法完成,因为目的库位Stock为视图。
  4. 修改Manufacture的路线规则,将成品库位设置为Stock/A.
  5. 报错,提示在Input库位没有找到合适的路线规则。
  6. 一脸懵逼,怎么会跟Input库位有关系,经排查,产品分类中设置了两步入库、两步出库,两步出库第二步Stock->Output后,Stock为目的库位的规则只有一个,就是两步入库中的Input-> Stock,因为产品没有选购买,因此找不到Input的下一步规则。
  7. 发现错误,新增Stock/A->Output的规则到Manufacture, 并没有生效。
  8. 查看代码,发现优先级问题,调整规则的优先级,成功。

排查的过程真是耗费了大量的时间,因此为了节省时间作为以后排查的依据,整理的结论如下:

路线规则

  1. 销售订单明细行 ——> 产品路线——> 产品分类路线——> 仓库路线
  2. 路线规则的优先级,首先看路线的序号,然后看规则的序号,取找到的第一个规则应用。

思考题

不知道你是否想过 官方的原生的MTO路线是如何实现的?如果让你手动定义一个MTO路线,你会怎么定义?

本例中,如何设置Stock/A->Output的规则,可以实现销售单做完以后,生成两步出库单,并自动在Stock/A上生成生产单?

Odoo Mixed Content错误处理方案

发表于 2020-07-29 | 分类于 odoo

Odoo服务器中使用了HTTPS方案后,在网站下单,出现如下错误:

1
Mixed Content: The page at 'https://' was loaded over HTTPS, but requested an insecure resource 'http://'. This request has been blocked; the content must be served over HTTPS.

出现问题的原因是在HTTPS中引用了HTTP的链接,导致安全警报.

解决方案:

  1. 在系统设置-技术参数-参数中将web.base.url 由http://转成https://
  2. 创建一个新参数 web.base.url.freeze并且设置为True.

Website中的翻译问题

如果使用了HTTPS的商城模块,有可能也会出现上面的错误,很有可能是Nginx的配置错误,解决方案是需要Ngnix中添加下面的配置:

1
proxy_redirect http:// $scheme://;

odoo 百度地图 视图模块

发表于 2020-06-12 | 分类于 odoo

本模块已适配12.0 13.0,14.0 已加入排期敬请关注。

最近项目比较忙,都没时间更新了,今天抽空给大家带来一个新的模块,百度地图视图。模块的起因是,有客户希望能在地图上展示出所有的客户的坐标,能够一眼看清某个客户附近的客户。企业版自带了一个地图模块,不过老外嘛,自然是使用Google Map了,由于复杂的网络原因,我们并不能直接使用,体验并不好,因此才有了今天的这个模块。

模块介绍

首先,我们要使用百度地图,就需要去百度地图的开放平台去注册一个账号,获取AK,填入到我们的模块参数中:

填完AK,其余的参数可以保持不动。
接下来,我们创建一个客户,并填入客户的详细地址:

该模块会在我们保存客户地址时,自动将客户地址逆解析为百度地图的坐标,并存在数据库中,并且会在地址栏的下方显示一个客户的缩小版的地图,可以拖拽,也可以缩放。

最后,我们可以在联系人模块中,看到在原有的视图类型中多出了一个百度地图类型的视图,我们点击它将能够在一张地图上看到所有的客户。

点击地图上的某个标记,可以看到与之对应的客户信息。

后续拓展

这些都是一些比较基础的功能,后续可以结合微信小程序等拓展出更多可能的应用来。后续有时间会陆续开发腾讯地图和高德地图的版本,敬请期待。

在一台服务器上安装多个Odoo实例

发表于 2020-06-01 | 分类于 odoo

生产环境下的安装可以使用之前文章讲述的快速安装方法,本文的目的是将多个版本的odoo安装到一台服务器上,实现多版本odoo的共存。

本文基于ubuntu 18.04

更新系统

1
2
3
4
sudo apt-get update && apt-get -y upgrade
sudo apt-get install git wkhtmltopdf python-pip python-dev \
python-virtualenv libevent-dev gcc libjpeg-dev libxml2-dev \
libssl-dev libsasl2-dev node-less libldap2-dev libxslt-dev

安装Postgresql

1
sudo apt-get install postgresql-10

创建odoo用户

先创建系统用户:

1
2
sudo adduser --system --group odoo13 --home /opt/odoo13
sudo adduser --system --group odoo12 --home /opt/odoo12

然后再创建 Postgresql用户:

1
sudo su - postgres -c "createuser --createdb --username postgres --no-createrole --no-superuser --no-password odoo12"

安装odoo源码

首先切换到我们刚才创建的系统用户:

1
sudo su - odoo13 -s /bin/bash

从github克隆源码到本地:

1
git clone https://www.github.com/odoo/odoo --depth 1 --branch 13.0 --single-branch /opt/odoo13

鉴于国内网络情况,推荐使用码云镜像克隆速度会提高很多。

创建Python 虚拟环境并安装依赖:

1
2
3
4
cd /opt/odoo13
python3 -m venv venv
source ./venv/bin/activate
pip3 install -r requirements.txt

同样的,推荐使用豆瓣源替代原生, https://pypi.douban.com/simple

重复上面的步骤以安装另外一个版本的odoo。

最后 退出当前odoo用户:

1
exit

配置odoo

创建odoo的配置文件:

1
sudo vim /etc/odoo13.conf
1
2
3
4
5
6
7
8
9
[options]
admin_passwd = your_strong_admin_password
db_host = False
db_port = False
db_user = odoo13
db_password = False
addons_path = /opt/odoo13/addons
logfile = /var/log/odoo13.log
xmlrpc_port = 8069

配置服务脚本

1
sudo vim /lib/systemd/system/odoo13.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Odoo 13
Requires=postgresql.service
After=postgresql.service

[Service]
Type=simple
PermissionsStartOnly=true
User=odoo13
Group=odoo13
SyslogIdentifier=odoo13
ExecStart=/opt/odoo13/venv/bin/python3 /opt/odoo13/odoo-bin -c /etc/odoo13.conf

[Install]
WantedBy=multi-user.target

同样的创建另外一个版本的odoo服务脚本

激活服务

1
sudo systemctl enable odoo13.service

自定义过滤器的快速搜索功能

发表于 2020-05-21 | 分类于 odoo

我们都知道odoo的自定义搜索功能比较实用,可以将所有存储类型的字段作为搜索条件,大多数情况下是比较好用的。今天要说的是当企业用了一段时间并自定了很多字段的情况下,字段列表将会变得非常长,这时候就会出现一个另用户比较头疼的痛点,想找一个字段太麻烦了,要盯着屏幕看好久。

这个时候我们就希望用户能够像快速编辑那样,通过输入部分名称,自动匹配到要搜索的字段,从而实现快速选择,节省用户查找时间。
因此,我们编写了Filter_Autocomplete模块,安装此模块以后,用户即可在自定义搜索栏实现模糊匹配,迅速找到自己想要的字段。

此模块目前支持 13.0和 10.0,已在笔者商店上架,欢迎选购。

odoo 快递鸟模块

发表于 2020-05-10 | 分类于 odoo

快递鸟提供了600+的物流公司对接接口,是比较不错的物流查询服务选择,本文将介绍由笔者写的一个快递鸟对接工具。有兴趣的同学可以通过本人的淘宝 进行选购。

设置快递鸟接口参数

使用这个模块的先决条件就是需要在快递鸟平台上注册一个开发者账号,并通过实名认证。然后得到一个APP KEY和SECRET。由这两个参数就可以调用快递鸟参数了。

同步 600+ 物流公司

安装本模块时,会自动加在受快递鸟支持的物流公司,您可以在业务中直接选择使用。

设置邮费支付方式

邮费支付方式支持现付、到付、月结、第三方支付。

您可以在各个快递的设置选项中进行选择。

设置电子面单格式

模块会在安装过程中自动加载受支持的电子面单格式,您可以在各个快递的设置中进行选择:

查询物流轨迹

在分拣单页面,填入物流公司和单号,点击获取物流轨迹,即可获取该单号的物流轨迹。

一键打印电子面单

在发货界面,当单据就绪时,您可以点击在线打印电子面单进行电子面单的打印操作。

出库单上直接打印电子面单,电子面单为网页格式,直接调用打印机打印即可。

批量打印电子面单

本模块同时支持电子面单的批量打印,详情参考用户手册。

物流商自动识别

在跟踪单号处填入物流单号,系统可以自动识别出物流商。

ubuntu 常用命令汇总

发表于 2020-05-07 | 分类于 ubuntu

##

du: 计算出单个文件或者文件夹的磁盘空间占用。
sort: 对文件行或者标准输出行记录排序后输出.
head: 输出文件内容的前面部分.

组合使用,查找某个文件夹下面的最大的10个文件夹:

1
du -ak / |sort -r -n|head -10

在centos7上安装odoo13

发表于 2020-05-05 | 分类于 odoo

安装

yum install epel-release

sudo root

yum -y groupinstall “Development tools”
yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel
yum install libffi-devel -y

cd /opt # 待安装的目录
wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz
tar -xvJf Python-3.7.0.tar.xz

如何将“业界毒瘤”闭源模块清除

发表于 2020-04-28 | 分类于 odoo

业界毒瘤

相信很多人之所以选择odoo就是因为它开源开放,虽然官方近些年来为了恰饭推出了企业版付费的模式,但节操仍在——企业版代码依旧开源开放。但是最近在客户项目中,突然发现了几个国产的微信公众号平台相关的模块,使用了闭源的方式——交付到客户手里的是加密过后的二进制代码。

这样做的带来的隐患有:

  • 源代码掌握在别人手中,出了问题只能找作者修改。
  • 一旦作者失联,项目将不可维护。
  • 数据产生之后将骑虎难下,议价权完全掌握在原作者手中。
  • 二进制代码不能Review,焉知其中没有后门或者在代码中下毒?

清除方法

首先,确认你没有生产数据在其中,否则,将会导致数据丢失。

闭源模块带来的问题之一就是本地调试极其不方便,代码出了问题无法调试。对于一个已经安装了闭源模块的数据库来说,我们可以通过一下步骤进行清除:

在数据库中,将闭源模块的安装状态设置为to remove:

1
update ir_module_module set state = 'to remove' where name ilike '%oe_jia%' and state='installed'

然后,重启服务,odoo将自动跳过这些模块,使你能够正常登录。

登录以后,找到应用,将这些毒瘤模块一一卸载清除:

呵呵,卸载时居然报错了:

忠告

不要使用不提供源码的第三方模块,因为这将对你的项目造成不可预估的风险,对你的金钱来说也可能是一笔无底洞,对后期接手项目的维护人员来说,更将是一场灾难。

123…23
长腿叔叔

长腿叔叔

长腿叔叔的技术博客

227 日志
37 分类
41 标签
GitHub 淘宝
友情链接
  • BUG集散地
© 2021 长腿叔叔
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.2