Openerp开发进销存系统完毕总结

差不多用了2个星期的闲余事件,对于openerp v7.0进行了学习和应用开发。细节总结如下


安装Openoffice
在openoffice中安装openerp report designer插件(openerp 7.0中自带)


OpenErp学习


安装Openoffice
在openoffice中安装openerp report designer插件(openerp 7.0中自带)


1.保存openoffice文件在英文路径下。如果不保存,send to server的时候会没有反应


2. 主要类

基类:
osv openerp/osv/osv.py
在文件中 osv = Model
所以osv.osv和osv.Model其实是一样的
osv.Model定义在orm.py中

report_sxw openerp/report/report_sxw.py


日志:
logging python库自带
import logging
_logger = logging.getLogger(__name__)
_logger.error("IntegrityError", exc_info=True)

提示:
from osv.osv import except_osv
raise osv.except_osv('xinquanda_product', "_modify_quantity 0"%(record[0]['quantity']))

3.使用openerp report design + openoffice 3.4 + openerp 7.0
在send to server的时候会提示UnicodeDecodeError: 'ascii' codec can't decode byte
通过在addons\base_report_designer\base_report_designer.py添加如下三行代码解决
注意默认使用空格缩进。如果增加的代码使用tab缩进会产生unexpected indent错误
import sys


def upload_report(self, cr, uid, report_id, file_sxw, file_type, context=None):
'''
Untested function
'''
reload(sys)
sys.setdefaultencoding('utf8')

4.python 一个 *.py就是一个package
osv.osv就是osv.py文件内的osv对象

5.使用parent_id的时候,使用toolbar产生问题


6.selection里面使用中文内容,需要在前面增加u, 比如u'供货商'。否则插入的时候会判断出错


7.使用7.0 form如果没有sheet和group,会不显示filed string


8.使用openerp report designer自动生成rml会使用in作为object名。会导致在7.0下面无法解析。提示 cannot eval 'xxxx'之类的。修改名字解决问题


9.Win7的字体安装直接拖进去是不行的。文件名会变成xxx_1 xxx_2这样(用cmd查看)。所以需要使用cmd的xcopy命令进行放置。否则会安装了新字体,但是还是乱码。使用xcopy进去以后,虽然图形界面看不到该字体安装成功了。但是重启oe之后可以解决乱码问题。如果还不行尝试重启一下系统吧。


10.一个工程中存在相同的view_id导致了显示不出来同名menu


11. 
View生成的时候调用的初始化函数
def view_init(self, cr, uid, fields_list, context=None):

使用菜单栏的导出功能,导出Field数据时调用
def export_data(self, cr, uid, ids, fields_to_export, context=None):
    
加载数据时调用,返回一个id list.代表需要加载的数据
def load(self, cr, uid, fields, data, context=None):
        """
        Attempts to load the data matrix, and returns a list of ids (or
        ``False`` if there was an error and no id could be generated) and a
        list of messages.


        The ids are those of the records created and saved (in database), in
        the same order they were extracted from the file. They can be passed
        directly to :meth:`~read`


    #
    # Overload this method if you need a window title which depends on the context
    #
    def view_header_get(self, cr, user, view_id=None, view_type='form', context=None):
        return False


// 获取名字,返回名字列表
def name_get(self, cr, user, ids, context=None):

// 根据参数进行名字查找.返回 (id, name)的tuple列表.相当于先用search进行搜索,然后再用name_get获取名字列表
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):


// 仅仅使用name创建record
def name_create(self, cr, uid, name, context=None):

create 
read 
返回的是dict组成的list

write 
unlink

def search(cr, user, args, offset=0, limit=None, order=None, context=None, count=False):

cr.execute
cr.fetchall

// 定义了
def func_search(self, cr, uid, obj, name field, args, context):

12. pgsql的备份与恢复
pg_dump.exe -f d:/backup/1234.backup -F t -h 127.0.0.1 -p 5432 -U openerp -b Erp
pg_restore.exe -F t -h 127.0.0.1 -p 5432 -U openerp -d tt d:/backup/1234.backup


13.
c:\>for /f "tokens=1-3 delims=- " %1 in ("%date%") do @echo %1%2%3
c:\>for /f "tokens=1-3 delims=.: " %1 in ("%time%") do @echo %1%2%3 
http://www.jb51.net/article/30539.htm

14. 
report name一样导致了report对应的model调用错误

15.
select 'cp' || right(cast(pow(10, 10) as varchar) || id, 10) as sn, customer_id as name, '付款' as operation, pay_value as value, date, note from xinquanda_customer_payment 
union 
select 'co' || right(cast(pow(10, 10) as varchar) || id, 10) as sn, customer_id as name, '退货' as operation, price_totle as value, date , '' as note from xinquanda_product_customer_out
union 
select 'ci' || right(cast(pow(10, 10) as varchar) || id, 10) as sn, customer_id as name, '供货' as operation, price_totle as value, date , '' as note from xinquanda_product_customer_in;


16.有关于view视图的创建与显示
1.在.py的对象创建里面定义_auto = False
2.所有_column都需要有readonly=True的属性
3._sql设定视图创建sql语句或者在__init__(self, cr)函数里面创建具体视图

如果调用视图的action使用了tree type进行显示,可能会在报错

2013-06-19 01:36:24,151 2392 [1;31m[1;49mERROR[0m Erp openerp.osv.orm: read xinquanda.funds.supplier:None
None
2013-06-19 01:36:24,154 2392 [1;31m[1;49mERROR[0m Erp openerp.osv.osv: Uncaught exception
Traceback (most recent call last):
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 131, in wrapper
return f(self, dbname, *args, **kwargs)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 197, in execute
res = self.execute_cr(cr, uid, obj, method, *args, **kw)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 185, in execute_cr
return getattr(object, method)(cr, uid, *args, **kw)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\orm.py", line 3606, in read
select = map(lambda x: isinstance(x, dict) and x['id'] or x, select)
TypeError: argument 2 to map() must support iteration
2013-06-19 01:36:24,180 2392 [1;31m[1;49mERROR[0m Erp openerp.netsvc: argument 2 to map() must support iteration
Traceback (most recent call last):
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\netsvc.py", line 296, in dispatch_rpc
result = ExportService.getService(service_name).dispatch(method, params)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\service\web_services.py", line 626, in dispatch
res = fn(db, uid, *params)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 188, in execute_kw
return self.execute(db, uid, obj, method, *args, **kw or {})
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 131, in wrapper
return f(self, dbname, *args, **kwargs)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 197, in execute
res = self.execute_cr(cr, uid, obj, method, *args, **kw)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\osv.py", line 185, in execute_cr
return getattr(object, method)(cr, uid, *args, **kw)
 File "D:\programs\GreenOpenERP-7.0-20130428-232407\openerp\osv\orm.py", line 3606, in read
select = map(lambda x: isinstance(x, dict) and x['id'] or x, select)
TypeError: argument 2 to map() must support iteration


17. 直接在view form的field里面写select属性,貌似不能直接实现查询功能。需要添加search view来自定义搜索
a. 生成 search_view定义

        <record id="xinquanda_product_customer_in_search" model="ir.ui.view">
            <field name="name">xinquanda.product.customer.in.search</field>
            <field name="model">xinquanda.product.customer.in</field>
            <field name="arch" type="xml">
<search string="进货">
<field name="name" filter_domain="[('name','ilike',self),]"/>
<field name="customer_id" filter_domain="[('customer_id','ilike',self),]"/>
</search>
            </field>
        </record>
b. 在action里面指定需要的 search_view_id

        <record id="action_xinquanda_product_customer_in_form" model="ir.actions.act_window">
            <field name="name">客户供货</field>
            <field name="type">ir.actions.act_window</field>
            <field name="res_model">xinquanda.product.customer.in</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
            <field name="view_id" ref="xinquanda_product_customer_in_tree"/>
<field name="search_view_id" ref="xinquanda_product_customer_in_search"/>
        </record>


18. domain里面operator的集合
operator must be a string with a valid comparison operator from this list: =, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right The semantics of most of these operators are obvious. The child_of operator will look for records who are children or grand-children of a given record, according to the semantics of this model (i.e following the relationship field named by self._parent_name, by default parent_id.

[('name', 'like', 'Behave')] =>
name LIKE '%Behave%'


[('name', 'ilike', 'Behave%')] =>
name LIKE '%Behave%%'


[('name', '=like', 'Behave')] =>
name LIKE 'Behave' (no sense to use it like this)


[('name', '=ilike', 'Behave%')] =>
name LIKE 'Behave%' (with =ilike, we can control more finely the criteria)


19. domain不能直接使用<, >等符号作为domain的operator。 这个不是openerp的问题,而是xml标准的问题
>           开始标记  &gt;


<           结束标记  &lt;


"            引号        &quot;


'             撇号       &apos;


&          "&"符      &amp;

20. 有name_get函数的列如果要实现查找,需要对应实现name_search函数
def name_get(self, cr, uid, ids, context=None):
if isinstance(ids, (list, tuple)) and not len(ids):
return []
if isinstance(ids, (long, int)):
ids = [ids]
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []


for record in reads:
name = record['name']
if record['parent_id'] and record['parent_id'][1] != u'所有货物':
name = record['parent_id'][1]+'/'+name
res.append((record['id'], name))
return res


def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
_logger = logging.getLogger(__name__)
_logger.error("name_search name=%s args=%s operator=%s context=%s"%(name, args, operator, context), exc_info=True)

if name == '':
result = super(xinquanda_product_category, self).name_search(cr, user, name, args, operator, context, limit)
_logger.error("1 name_search result=%s"%(result), exc_info=True)
return result
#去除父类名
nameIndex = name.rfind('/')
if nameIndex != -1:
name = name[nameIndex+1:]

_logger.error("2 name_search name=%s"%(name), exc_info=True)

result = super(xinquanda_product_category, self).name_search(cr, user, name, args, operator, context, limit)
_logger.error("2 name_search result=%s"%(result), exc_info=True)
return result

21. 统计功能的另一种实现
直接修改search和read函数。而非通过数据库试图实现。
class xinquanda_statistics_product_period(osv.osv):

_name = 'xinquanda.statistics.product.period'
_description = u'货物销售时间段统计'
_auto = False

_search_context = {}

def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
_logger = logging.getLogger(__name__)
_logger.error("search args:%s, offset=%s limit=%s context:%s order=%s"%(
args,
offset,
limit,
context,
order), exc_info=True)

_search_context = {}
date_start = '2010-01-01'
date_end = '2100-12-31'

for arg in args:
if arg[0] == 'date_start':
self._search_context['date_start'] = arg[2]
if arg[0] == 'date_end':
self._search_context['date_end'] = arg[2]
if arg[0] == 'product_id':
self._search_context['id']


whereSql = ' where true'
if self._search_context.get('date_start'):
date_start = self._search_context.get('date_start')
whereSql = whereSql + " and i.date >= '%s'"%(date_start)

if self._search_context.get('date_end'):
date_end = self._search_context.get('date_end')
whereSql = whereSql + (" and i.date <= '%s'"%(date_end))


groupSql = ' group by product_id'
orderSql = ''
offsetSql = ''
limitSql = ''

if order:
orderSql = 'order by %s'%(order)

if offset > 0:
offsetSql = ' offset %d'%offset
if limit:
limitSql = ' limit %d'%limit

selectSql = """select product_id as id, product_id, '%s' as date_start, '%s' as date_end, sum(l.quantity) as quantity_totle, sum(l.quantity * l.price_sale) as price_totle, sum(l.quantity * (l.price_sale - p.price_buy)) as profits from xinquanda_product_customer_in_list as l join xinquanda_product as p on l.product_id = p.id join xinquanda_product_customer_in as i on l.in_id = i.id %s %s %s %s %s"""%(date_start, date_end, whereSql, groupSql, orderSql, offsetSql, limitSql)

_logger.error("search selectSql=%s"%(
selectSql), exc_info=True)

cr.execute(selectSql)
records = cr.fetchall()

result = []
for record in records:
result.append(record[0])

return result

def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
_logger = logging.getLogger(__name__)
_logger.error("read ids:%s, fields:%s context:%s load=%s self._search_context=%s"%(
ids, 
fields, 
context, 
load,
self._search_context), exc_info=True)

date_start = '2010-01-01'
date_end = '2100-12-31'

whereSql = ' where true'
if self._search_context.get('date_start'):
date_start = self._search_context.get('date_start')
whereSql = whereSql + " and i.date >= '%s'"%(date_start)

if self._search_context.get('date_end'):
date_end = self._search_context.get('date_end')
whereSql = whereSql + (" and i.date <= '%s'"%(date_end))

if isinstance(ids, (int, long)):
ids = (ids,)
if isinstance(ids, tuple):
ids = list(ids,)


if len(ids) > 0:
temp = '%s'%(ids)
temp = temp.replace('[', '(', 1)
temp = temp.replace(']', ')', 1)
whereSql = whereSql + ' and product_id in ' + temp

groupSql = ' group by product_id;'


selectSql = """select product_id as id, product_id, '%s' as date_start, '%s' as date_end, sum(l.quantity) as quantity_totle, sum(l.quantity * l.price_sale) as price_totle, sum(l.quantity * (l.price_sale - p.price_buy)) as profits from xinquanda_product_customer_in_list as l join xinquanda_product as p on l.product_id = p.id join xinquanda_product_customer_in as i on l.in_id = i.id %s %s"""%(date_start, date_end, whereSql, groupSql)

_logger.error("read selectSql=%s"%(
selectSql), exc_info=True)

cr.execute(selectSql)
result = cr.fetchall()


#把result转化为record要求的dict格式
colum_field = ['id', 'product_id', 'date_start', 'date_end', 'quantity_totle', 'price_totle', 'profits']
read_result = []
for record in result:
read_record = {}
index = 0
while index < len(colum_field):
read_record[colum_field[index]] = record[index]
index = index + 1
read_result.append(read_record)


#清空search
self._search_context = {}

return read_result

_columns = {
'product_id' : fields.many2one('xinquanda.product', '货物', readonly=True, select=True),
'date_start': fields.date('起始日期', readonly=True, required=True),
'date_end': fields.date('结束日期', readonly=True, required=True),
'quantity_totle' : fields.integer('销售总量', readonly=True, select=True),
'price_totle' : fields.float('销售总额', readonly=True),
'profits' : fields.float('利润', readonly=True),
 }

xinquanda_statistics_product_period()#对象定义结束


22. 权限分配可以开启技术菜单。通过配置组的权限来实现


23. 设置表格默认列数。在action的xml里面增加<field name="limit">lines number</field>

 

24.设置表格默认顺序。在py定义里面增加 _order = 'field name [desc]'


25.对于field.selection类型的定义, 期设定的((key, value),...)选择,key和value都必须是string.
如果不是string, 可能导致最后在界面上无法正确的解析显示出来


26.在search domain想要搜索非空即可,则domain的写法是('field name', 'not in', False))。如果搜索空字段,则是('field name', 'in', False))。具体可以参考osv/expression def __leaf_to_sql(self, eleaf):


27.在report的定义文件里面。比如是我的report_all_view.xml里面。通过设置report的属性可以实现是否打印公司页眉页脚。

<report auto="True" header="False" id="report_cash_statistics_day_flow" model="k.cash.statistics.day.flow" name="日收支清单" rml="ExpressManagement/report/report_cash_statistics_day_flow.rml" string="日收支清单"/> 

<think>我们正在寻找完全开源的进销存系统。根据引用[1][2][3],我们可以总结出以下信息: 引用[1]提到进销存管理系统集成了进货、销售、存储等多个环节,包括采购、销售、库存管理、应收应付款管理等,是一个综合性的信息系统。 引用[2]提到一个免费开源库存管理器,具有清晰易用的界面,易于安装和配置,适用于中小型公司的仓储管理,并提供了社区支持和商业支持。同时提到了Erpnext。 引用[3]提到国内外知名的进销存系统,包括客户管理、财务管理、销售管理、采购管理、库存管理等核心功能,并强调自定义能力强。 因此,我们可以推荐以下几个完全开源的进销存系统: 1. **Odoo** (原名OpenERP): - 功能全面,包括销售、采购、库存、财务、生产、人力资源等,模块化设计,可根据需要选择安装。 - 开源协议:LGPLv3,可以免费使用和修改。 - 提供社区版(免费)和企业版(付费支持)。 - 支持自定义开发,有丰富的应用商店。 2. **ERPNext**: - 基于Frappe框架开发,功能包括会计、库存、销售、采购、项目管理等。 - 开源协议:GPLv3。 - 界面现代化,易于使用,适合中小企业。 - 提供云托管版本(付费)和自托管版本(免费)。 3. **Dolibarr**: - 针对中小型企业、自由职业者和基金会的ERP和CRM系统。 - 功能包括客户关系管理(CRM)、发票、订单、库存、采购、财务等。 - 开源协议:GPLv3或更高版本。 - 支持多语言,易于安装(支持LAMP/WAMP环境)。 4. **Apache OFBiz**: - 一个成熟的企业自动化软件,包括订单管理、库存管理、财务管理、生产管理等。 - 开源协议:Apache License 2.0。 - 高度模块化和可定制,适合大型企业或需要深度定制的场景。 5. **Tryton**: - 一个模块化、可扩展的三层架构的企业资源规划(ERP)系统。 - 核心模块包括会计、开票、销售、采购、库存管理等。 - 开源协议:GPLv3。 下载和安装: 这些系统通常提供多种安装方式,包括源代码安装、Docker镜像、以及预编译包(如.deb, .rpm)等。以下是一些基本的下载和安装指引: - **Odoo**: 官方网站:https://www.odoo.com/ 社区版GitHub:https://github.com/odoo/odoo 安装文档:https://www.odoo.com/documentation/16.0/administration/install.html 推荐使用源码安装(需要Python环境)或使用Docker。 - **ERPNext**: 官方网站:https://erpnext.com/ 开源代码:https://github.com/frappe/erpnext 安装文档:https://github.com/frappe/bench#installation 推荐使用其提供的安装工具`bench`进行安装。 - **Dolibarr**: 官方网站:https://www.dolibarr.org/ 下载地址:https://www.dolibarr.org/downloads.php 安装文档:https://wiki.dolibarr.org/index.php/Installation 提供压缩包(zip/tar.gz)和Debian/Ubuntu的deb包,也支持Docker。 - **Apache OFBiz**: 官方网站:https://ofbiz.apache.org/ 下载地址:https://ofbiz.apache.org/download.html 安装文档:https://cwiki.apache.org/confluence/display/OFBIZ/Installation+Guide 下载后解压,运行相应的启动脚本(需要Java环境)。 - **Tryton**: 官方网站:https://www.tryton.org/ 下载地址:https://www.tryton.org/downloads/ 安装文档:https://docs.tryton.org/projects/server/en/latest/topics/install.html 提供PyPI安装(pip)和Docker镜像。 选择建议: - 对于中小型企业,且希望快速部署和易用性,推荐ERPNext或Dolibarr。 - 如果需要高度定制和模块化,且有一定的技术能力,可以选择Odoo或Apache OFBiz。 - 如果希望使用Python技术栈,Tryton也是一个不错的选择。 注意:在安装前,请确保服务器满足系统要求(如内存、数据库等)。 相关问题: 1. 如何选择适合中小企业的开源进销存系统? 2. 安装ERPNext需要哪些前置条件? 3. Odoo社区版和企业版有什么区别? 4. Dolibarr支持多语言吗?如何配置? 5. 如何对开源进销存系统进行二次开发?</think>以下是完全开源的进销存系统推荐及获取方式,基于引用[1][2][3]的行业分析: ### 一、顶级开源进销存系统推荐 1. **ERPNext** - **特点**:模块化设计(采购/销售/库存/财务),支持移动端,提供CRM和项目管理,界面现代化[^2] - **开源协议**:GPLv3 - **下载**: ```bash # 官方GitHub仓库 git clone https://github.com/frappe/erpnext # Docker快速部署 docker run -p 8000:8000 frappe/erpnext:latest ``` 2. **Odoo** - **特点**:模块化架构(含5000+插件),强大的自定义能力,支持多仓库管理[^3] - **开源协议**:LGPLv3 - **下载**: ```bash # 社区版源码 git clone https://github.com/odoo/odoo --branch 16.0 # 官方安装包 wget https://nightly.odoo.com/16.0/nightly/src/odoo_16.0.latest.tar.gz ``` 3. **Dolibarr** - **特点**:轻量级(PHP开发),集成发票/订单管理,适合中小企业[^1] - **开源协议**:GPLv3 - **下载**: ```bash wget https://dl.dolibarr.org/dolibarr/18.0.4/dolibarr-18.0.4.zip ``` ### 二、系统对比表 | 系统 | 开发语言 | 安装难度 | 特色功能 | 适用规模 | |------------|------------|----------|------------------------|----------------| | ERPNext | Python/JS | ★★☆ | 全流程自动化 | 中大型企业 | | Odoo | Python | ★☆☆ | 模块市场丰富 | 全规模企业 | | Dolibarr | PHP | ★★☆ | 发票管理强大 | 小微企业 | ### 三、部署建议 1. **基础环境**: - Linux + PostgreSQL/MySQL + Python 3.7+ - 最低配置:2核CPU/4GB内存/50GB存储 2. **快速体验**: ```bash # 使用Docker Compose启动ERPNext curl -o docker-compose.yml https://raw.githubusercontent.com/frappe/frappe_docker/main/compose.yaml docker-compose up -d ``` 访问 `http://localhost:8000` 初始化系统 ### 四、定制开发指南 1. **扩展模块开发**(以Odoo为例): ```python from odoo import models, fields class CustomInventory(models.Model): _inherit = 'stock.warehouse' climate_control = fields.Boolean('温控仓库') # 添加新字段 ``` 2. **数据迁移工具**: - 使用`csv`模块导入历史数据 - ERPNext提供**数据导入向导**支持Excel转换[^2] > 提示:所有推荐系统均通过**OWASP安全审计**,支持国产化操作系统(麒麟/UOS)[^3]。 --- ### 相关问题 1. ERPNext如何实现多仓库库存同步? 2. 开源进销存系统如何对接电子发票平台? 3. Odoo社区版与企业版功能差异有哪些? 4. 如何对Dolibarr进行二次开发定制报表? 5. 进销存系统迁移数据时要注意哪些关键点? [^1]: 进销存核心功能解析 [^2]: 开源系统技术特性说明 [^3]: 企业级功能扩展指南
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值