Git 通过Commit关闭Issue

如果要在相同仓库关闭一个Issue(工单),可以使用下面列表中的关键词并在其后面加上Issue编号的应用(#+issue编号)。

例如一个提交信息中包含 Fixes #45 ,那么一旦这次提交被合并到默认分支,仓库中的45号issue就会自动关闭。

Issue 关键字

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

如果在不同的仓库中关闭issue,可以使用 username/repository#issue_number 这样的语法。

例如,在提交信息中包含Closes example_user/example_repo#76将会关闭那个仓库的的76号issue,前提是你push到了那个仓库。

关闭多个issues

如果你在commit的开头使用多个上述关键字加issue的引用的话,你就可以关闭多个issues。

例如,This closes #34, closes #23, and closes example_user/example_repo#42将会关闭同一个仓库的34和23号issue以及 example_user/example_repo 仓库的42号issue。

WinCE 同步时间

最近项目中PDA的时间总是与业务数据时间不符,因此需要一个这样的功能。

首先确定使用WebService的Response信息作为时间的获取源头。

代码如下

#region 修改系统日期
[DllImport("coredll.dll")]
private static extern bool SetLocalTime(ref SystemTime lpSystemTime);

public struct SystemTime
{
    public short wYear;
    public short wMonth;
    public short wDayOfWeek;
    public short wDay;
    public short wHour;
    public short wMinute;
    public short wSecond;
    public short wMilliseconds;
}

/// <summary>
/// 设置系统当前日期
/// </summary>
public static void SetTime()
{

    WebRequest request = null;
    WebResponse response = null;
    WebHeaderCollection headerCollection = null;

    string webserviceUrl = string.Format("http://{0}/axis/servlet/AxisServlet", DB.GetServerAddr());
    string datetime = string.Empty;
    CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture("en-US");
    // Tue, 11 Sep 2018 09:46:58 GMT
    string format = "r";

    try
    {
        request = WebRequest.Create(webserviceUrl);
        request.Timeout = -1;
        request.Credentials = CredentialCache.DefaultCredentials;
        response = request.GetResponse();
        headerCollection = response.Headers;
        foreach (var item in headerCollection.AllKeys)
        {
            if (item.Equals("Date",StringComparison.CurrentCultureIgnoreCase))
            {
                datetime = headerCollection[item];
            }
        }

        SystemTime newTime = new SystemTime();
        DateTime dt = DateTime.ParseExact(datetime, format, cultureInfo).AddHours(8.00);

        newTime.wYear = Convert.ToInt16(dt.Year);
        newTime.wMonth = Convert.ToInt16(dt.Month);
        newTime.wDay = Convert.ToInt16(dt.Day);
        newTime.wDayOfWeek = Convert.ToInt16(dt.DayOfWeek);
        newTime.wHour = Convert.ToInt16(dt.Hour);
        newTime.wMinute = Convert.ToInt16(dt.Minute);
        newTime.wSecond = Convert.ToInt16(dt.Second);

        SetLocalTime(ref newTime);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        if (request != null)
        {
            request.Abort();
        }
        if (response != null)
        {
            response.Close();
        }
        if (headerCollection != null)
        {
            headerCollection.Clear();
        }
    }
}
#endregion

Odoo Widget 颜色的使用

Tree视图定义行的颜色

<record id="purchase_order_tree" model="ir.ui.view">
	<field name="name">purchase.order.tree</field>
	<field name="model">purchase.order</field>
	<field name="arch" type="xml">
		<tree decoration-bf="message_unread==True" decoration-muted="state=='cancel'" decoration-info="state in ('wait','confirmed')" string="Purchase Order">
			<field name="message_unread" invisible="1"/>
			<field name="name" string="Reference"/>
			<field name="date_order" />
			<field name="partner_id"/>
			<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
			<field name="date_planned" invisible="context.get('quotation_only', False)"/>
			<field name="origin"/>
			<field name="amount_untaxed" sum="Total Untaxed amount" string="Untaxed" widget="monetary"/>
			<field name="amount_total" sum="Total amount" widget="monetary"/>
			<field name="currency_id" invisible="1"/>
			<field name="state"/>
			<field name="invoice_status" invisible="not context.get('show_purchase', True)"/>
		</tree>
	</field>
</record>

Kanban组件

color值为0~9

<li><ul class="oe_kanban_colorpicker" data-field="color"/></li>

示例:

<record id="view_forum_post_kanban" model="ir.ui.view">
	<field name="name">forum.post.kanban</field>
	<field name="model">forum.post</field>
	<field name="arch" type="xml">
		<kanban default_group_by="documentation_stage_id" >
			<field name="documentation_stage_id"/>
			<field name="create_uid"/>
			<field name="color"/>
			<templates>
			<t t-name="kanban-box">
				<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
					<div class="o_dropdown_kanban dropdown" groups="base.group_user">
						<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
							<li t-if="widget.editable"><a type="edit">Edit Post</a></li>
							<li t-if="widget.deletable"><a type="delete">Delete</a></li>
							<li><ul class="oe_kanban_colorpicker" data-field="color"/></li>
						</ul>
					</div>
				</div>
			</t>
			</templates>
		</kanban>
	</field>
</record>

Many2many_tag widget

值范围:1~12

<field name="about_place_route" widget="many2many_tags" options="{'color_field': 'color', 'no_open':True,'no_create':1,'no_create_edit':1}" />


啊啊

C# 自动导入数据至数据库

最后在写一个项目,数据源自NC接口,需要将NC接口返回的数据处理后导入Sqlite数据库。

在实际操作中需要直接写入Datatable的对象进数据库,或直接将一行数据库写入数据,但每次构建SQL这些太麻烦。因此才有了这样一个处理类。

构建SQL

/// <summary>
/// 根据DataTable构建插入语句
/// </summary>
/// <param name="dataTable"></param>
/// <returns></returns>
public static string builderSql(DataTable dataTable)
{
    StringBuilder sb = new StringBuilder("insert into "+ dataTable.TableName + "(");
    StringBuilder head = new StringBuilder();
    StringBuilder values = new StringBuilder();
    foreach (DataColumn col in dataTable.Columns)
    {
        head.Append(col.ColumnName + ",");
        values.Append("@" + col.ColumnName + ",");
    }
    head.Remove(head.Length - 1, 1);
    values.Remove(values.Length - 1, 1);
    sb.Append(head).Append(") VALUES (").Append(values).Append(");");
    sb.Append("select last_insert_rowid();");
    return sb.ToString();
}

插入行

/// <summary>
/// 插入行
/// </summary>
/// <param name="row"></param>
/// <returns>数据库ID</returns>
public static int InsertDataRow(DataRow row) 
{
    using (SQLiteConnection connection = new SQLiteConnection(connectionString))
    {
        using (SQLiteCommand cmd = new SQLiteCommand())
        {
            try
            {
                string SQLString = builderSql(row.Table);

                List<SQLiteParameter> sqlitePars = new List<SQLiteParameter>();
                foreach (DataColumn col in row.Table.Columns)
                {
                    SQLiteParameter pars = new SQLiteParameter();
                    pars.ParameterName = string.Format("@{0}", col.ColumnName);
                    pars.Value = row[col.ColumnName];
                    pars.DbType = GetDbType(col.DataType);
                    sqlitePars.Add(pars);
                }

                PrepareCommand(cmd, connection, null, SQLString, sqlitePars.ToArray());

                int rows = Convert.ToInt32(cmd.ExecuteScalar());

                cmd.Parameters.Clear();
                return rows;
            }
            catch (System.Data.SQLite.SQLiteException sqlex)
            {
                throw sqlex;
            }
        }
    }
}

批量写入

/// <summary>
/// 批量插入
/// </summary>
/// <param name="dataTable">需插入数据库的表</param>
/// <param name="DataBaseName">数据库名称</param>
/// <returns></returns>
public static void InsertDataTable(DataTable dataTable)
{
    using (SQLiteConnection conn = new SQLiteConnection(connectionString))
    {
        conn.Open();
        using (SQLiteTransaction trans = conn.BeginTransaction())
        {
            SQLiteCommand cmd = new SQLiteCommand();
            try
            {
                // 构建插入语句
                string cmdText = builderSql(dataTable);

                //循环
                foreach (DataRow row in dataTable.Rows)
                {
                    string BaseName = dataTable.TableName;

                    // 构建插入值
                    List<SQLiteParameter> sqlitePars = new List<SQLiteParameter>();
                    foreach (DataColumn col in dataTable.Columns)
                    {
                        SQLiteParameter pars = new SQLiteParameter();
                        pars.ParameterName = string.Format("@{0}", col.ColumnName);
                        pars.Value = row[col.ColumnName];
                        pars.DbType = GetDbType(col.DataType);
                        sqlitePars.Add(pars);
                    }
                    PrepareCommand(cmd, conn, trans, cmdText, sqlitePars.ToArray());
                    int val = cmd.ExecuteNonQuery();
                    cmd.Parameters.Clear();
                }
                trans.Commit();
            }
            catch
            {
                trans.Rollback();
                throw;
            }
        }
    }
}

自动获取对象数据类型

private static Dictionary typeMap;

#region 类型对应
static SQLite()
{
    typeMap = new Dictionary();

    typeMap[typeof(string)] = DbType.String;
    typeMap[typeof(byte)] = DbType.Byte;
    typeMap[typeof(int)] = DbType.Int32;
    typeMap[typeof(Int16)] = DbType.Int16;
    typeMap[typeof(Int32)] = DbType.Int32;
    typeMap[typeof(Int64)] = DbType.Int64;
    typeMap[typeof(DateTime)] = DbType.DateTime;
    typeMap[typeof(decimal)] = DbType.Decimal;
    typeMap[typeof(float)] = DbType.Double;
    typeMap[typeof(double)] = DbType.Double;
}

public static DbType GetDbType(Type giveType)
{
    giveType = Nullable.GetUnderlyingType(giveType) ?? giveType;

    if (typeMap.ContainsKey(giveType))
    {
        return typeMap[giveType];
    }

    throw new ArgumentException(string.Format("{0} is not a supported .NET class", giveType.FullName));
}

public static DbType GetDbType()
{
    return GetDbType(typeof(T));
}

#endregion

Odoo 打印开发

一、声明报告Actions

参考链接:QWeb Reports

<report
        id="budget_report"
        model="budget.budget"
        report_type="qweb-pdf"
        string="Budget Report"
        name="contract_manager.budget_report_template"
        file="contract_manager.budget_report_template"
        paperformat="budget_print_format"
        menu="True"
/>

定义纸张

参考链接:Paper Format

<record id="budget_print_format" model="report.paperformat">
	<field name="name">Budget Print format</field>
	<field name="format">A4</field>
	<field name="orientation">Landscape</field> <!--方向-->
	<field name="margin_top">3</field>
	<field name="margin_bottom">3</field>
	<field name="margin_left">3</field>
	<field name="margin_right">3</field>
	<field name="header_line" eval="False" />
	<field name="header_spacing">3</field>
	<field name="dpi">90</field>
</record>

定义模板

<template id="report_invoice">
    <t t-call="report.html_container">
        <t t-foreach="docs" t-as="o">
            <t t-call="report.external_layout">
                <div class="page">
                    <h2>Report title</h2>
                    <p>This object's name is <span t-field="o.name"/></p>
                </div>
            </t>
        </t>
    </t>
</template>

调用report.external_layout将在报告上添加默认的页眉和页脚,正文将是其中的<div class="page">

报告中可以访问的一些特定变量,主要是:

  • docs  当前报告的数据记录集
  • doc_ids 记录的id列表
  • doc_model docs记录的模型
  • time 来自Python标准库的引用
  • user res.user用户打印报告的记录
  • res_company 当前user的公司

自定义报告

使用自定义报告需要具有一个默认的 get_html 函数,如果你希望在模板中包含更多的内容(例如,其他模型记录)来自定义报告,则可以定义此模型,覆盖函数并在字典中传递对象:

report.module.report_name render_html docargs

from odoo import api, models

class ParticularReport(models.AbstractModel):
    _name = 'report.module.report_name'
    @api.model
    def render_html(self, docids, data=None):
        report_obj = self.env['report']
        report = report_obj._get_report_from_name('module.report_name')
        docargs = {
            'doc_ids': docids,
            'doc_model': report.model,
            'docs': self,
        }
        return report_obj.render('module.report_name', docargs)