* LocalizedText.fr.resx: internationalisation de la saisie de

l'estimation

* LocalizedText.resx: 

* FrontOfficeApiController.cs: renamed the Tex generation method

* FrontOfficeController.cs: fixed the Estimate creation

* WorkFlowController.cs: return model validation errors when updating
  a writting.

* TexFormatter.cs: Simple mime-type content declaration

* Global.asax.cs:
* T4TemplateEstimate.cs: cleanning

* BBCodeHelper.cs: BBCodes: docpage summary gone into a new aside
  element

* App.master: thanks links are now contained in a div element

* style.css: clearer

* Estimate.aspx: Fixed the creation/edition/removal processes

* Estim.tt: added the column "Count" to the writtings table.

* RegisterViewModel.cs: internationalization

* Writting.cs: stronger model

* Estim.tex: cleaning
vnext
Paul Schneider 10 years ago
parent b4fefbce7e
commit 312585d4f0
16 changed files with 130 additions and 244 deletions

@ -20,4 +20,11 @@
<data name="Offline"><value>Hors ligne</value></data>
<data name="Not Approuved"><value>Non approuvé</value></data>
<data name="Remove"><value>Supprimer</value></data>
<data name="Pdf_version"><value>Version Pdf</value></data>
<data name="Tex_version"><value>Version LaTeX</value></data>
<data name="User_name"><value>Nom d'utilisateur</value></data>
<data name="Description"><value>Description</value></data>
<data name="Product_reference"><value>Référence produit</value></data>
<data name="Unitary_cost"><value>Coût unitaire</value></data>
<data name="Count"><value>Nombre</value></data>
</root>

@ -20,4 +20,12 @@
<data name="Offline"><value>Offline</value></data>
<data name="Not Approuved"><value>Not Approuved</value></data>
<data name="Remove"><value>Remove</value></data>
<data name="Pdf_version"><value>Pdf version</value></data>
<data name="Tex_version"><value>LaTeX version</value></data>
<data name="User_name"><value>User name</value></data>
<data name="Description"><value>Description</value></data>
<data name="Product_reference"><value>Product_reference</value></data>
<data name="Unitary_cost"><value>Unitary_cost</value></data>
<data name="Count"><value>Count</value></data>
</root>

@ -86,16 +86,16 @@ namespace Yavsc.ApiControllers
}
[AcceptVerbs("GET")]
public HttpResponseMessage GetTexEstim(long estimid)
public HttpResponseMessage GetEstimTex(long estimid)
{
return new HttpResponseMessage () {
Content = new ObjectContent (typeof(string),
getTexEstim (estimid),
new TexFormatter ())
getEstimTex (estimid),
new SimpleFormatter ("text/x-tex"))
};
}
private string getTexEstim(long estimid)
private string getEstimTex(long estimid)
{
Yavsc.templates.Estim tmpe = new Yavsc.templates.Estim();
Estimate e = WorkFlowManager.GetEstimate (estimid);

@ -30,6 +30,8 @@ namespace Yavsc.Controllers
[Authorize]
public ActionResult Estimate(Estimate model,string submit)
{
ViewData ["WebApiBase"] = "http://" + Request.Url.Authority + "/api";
ViewData ["WABASEWF"] = ViewData ["WebApiBase"] + "/WorkFlow";
if (submit == null) {
if (model.Id > 0) {
Estimate f = WorkFlowManager.GetEstimate (model.Id);
@ -46,10 +48,13 @@ namespace Yavsc.Controllers
throw new UnauthorizedAccessException ("You're not allowed to view this estimate");
}
} else if (ModelState.IsValid) {
ViewData ["WebApiUrl"] = "http://" + Request.Url.Authority + "/api/WorkFlow";
} else {
string username = HttpContext.User.Identity.Name;
if (model.Id == 0) {
model.Responsible=username;
ModelState.Clear ();
}
if (ModelState.IsValid) {
if (username != model.Responsible
&& username != model.Client
&& !Roles.IsUserInRole ("FrontOffice"))
@ -63,6 +68,7 @@ namespace Yavsc.Controllers
WorkFlowManager.UpdateEstimate (model);
}
}
return View(model);
}

@ -38,14 +38,17 @@ namespace Yavsc.ApiControllers
{
WorkFlowManager.DropWritting (wrid);
}
class Error {}
[Authorize]
[AcceptVerbs("POST")]
public void UpdateWritting([FromBody] Writting wr)
public object UpdateWritting([FromBody] Writting model)
{
if (!ModelState.IsValid)
throw new Exception ("Modèle invalide");
WorkFlowManager.UpdateWritting (wr);
if (!ModelState.IsValid) {
return ModelState.Where ( k => k.Value.Errors.Count>0) ;
}
WorkFlowManager.UpdateWritting (model);
return null;
}
[HttpGet]
@ -67,8 +70,12 @@ namespace Yavsc.ApiControllers
[AcceptVerbs("POST")]
[Authorize]
public long Write ([FromUri] long estid, [FromBody] Writting wr) {
// TODO ensure estid owner matches the current one
/// <summary>
/// Adds the specified imputation to the given estimation by estimation id.
/// </summary>
/// <param name="estid">Estimation identifier</param>
/// <param name="wr">Imputation to add</param>
public long Write ([FromUri] long estid, Writting wr) {
return WorkFlowManager.Write(estid, wr.Description,
wr.UnitaryCost, wr.Count, wr.ProductReference);
}

@ -27,12 +27,11 @@ using System.Net.Http;
namespace Yavsc.Formatters
{
public class TexFormatter : BufferedMediaTypeFormatter
public class SimpleFormatter : BufferedMediaTypeFormatter
{
public TexFormatter ()
public SimpleFormatter (string mimetype)
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/x-tex"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue(mimetype));
}
public override bool CanWriteType(System.Type type)

@ -44,7 +44,7 @@ namespace Yavsc
protected void Application_Start ()
{
AreaRegistration.RegisterAllAreas ();
GlobalConfiguration.Configuration.Formatters.Add (new TexFormatter ());
// add formatters : GlobalConfiguration.Configuration.Formatters.Add (new ZeroFormatter ());
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{*id}",

@ -148,7 +148,9 @@ namespace Yavsc.Helpers
}
}
}
return ttb.ToString ();
TagBuilder aside = new TagBuilder ("aside");
aside.InnerHtml = ttb.ToString ();
return aside.ToString();
}
static string DocPageContentTransformer (string instr)

@ -56,7 +56,7 @@
</aside>
<footer>
<% foreach ( string link in Yavsc.ThanksHelper.Links()) { %>
<%= link %>
<div><%= link %></div>
<% } %>
</footer>
</body>

@ -12,6 +12,10 @@ main, aside {
background-color: rgba(0,0,0,0.8);
}
aside {
display:inline;
max-width:15em;
}
video,img {
max-width:100%;
max-height:75%;
@ -24,7 +28,8 @@ footer {
left:0;
right:0;
background-color:rgba(0,16,0,0.6);
text-align:center;
display: flex;
justify-content: space-around;
z-index:-1;
}

@ -14,10 +14,7 @@
<%= Html.LabelFor(model => model.Title) %>:<%= Html.TextBox( "Title" ) %>
<%= Html.ValidationMessage("Title", "*") %>
<br/>
<%= Html.LabelFor(model => model.Responsible) %>:<%=Model.Responsible%>
<%= Html.Hidden ("Responsible") %>
<%= Html.ValidationMessage("Responsible", "*") %>
<br/>
<%= Html.LabelFor(model => model.Client) %>:<%=Html.TextBox( "Client" ) %>
<%= Html.ValidationMessage("Client", "*") %>
<br/>
@ -36,14 +33,13 @@
<% if (Model.Id>0) { %>
<table class="tablesorter">
<thead>
<tr>
<th>Description</th>
<th>Product Reference</th>
<th>Count</th>
<th>Unitary Cost</th>
<th><%=LocalizedText.Description%></th>
<th><%=LocalizedText.Product_reference%></th>
<th><%=LocalizedText.Count%></th>
<th><%=LocalizedText.Unitary_cost%></th>
</tr>
</thead>
<tbody id="wrts">
@ -59,11 +55,8 @@
<% } %>
</tbody>
</table>
<% } %>
<% } %>
</asp:Content>
<asp:Content ContentPlaceHolderID="MASContent" ID="MASContent1" runat="server">
@ -85,7 +78,7 @@
<input type="button" id="btnnew" value="Nouvelle écriture"/>
<input type="hidden" name="estid" id="estid" value="<%=Model.Id%>"/>
<label for="Description">Description:</label>
<input type="text" name="Description" id="Description" />
<textarea name="Description" id="wrdesc" ></textarea>
<label for="UnitaryCost">Prix unitaire:</label>
<input type="number" name="UnitaryCost" id="UnitaryCost" step="0.01"/>
<label for="Count">Quantité:</label>
@ -97,7 +90,7 @@
<input type="button" name="btndrop" id="btndrop" value="Supprimer"/>
<input type="hidden" name="wrid" id="wrid" />
<tt id="msg" class="message"></tt>
<tt id="msg" class="message" style="display:none;"></tt>
<style>
.row { cursor:pointer; }
table.tablesorter td:hover { background-color: rgba(0,64,0,0.5); }
@ -108,19 +101,24 @@
<script>
jQuery.support.cors = true;
function message(msg) {
if (msg) { $("#msg").css("display:inline");
$("#msg").text(msg);} else { $("#msg").css("display:hidden"); } }
function GetWritting () {
return {
Id: Number($("#wrid").val()),
UnitaryCost: Number($("#UnitaryCost").val()),
Count: parseInt($("#Count").val()),
ProductReference: $("#ProductReference").val(),
Description: $("#Description").val()
Description: $("#wrdesc").val()
};
}
function wredit(pwrid)
{
$("#wr"+wrid.value).removeClass("selected");
$("#wrid").val(pwrid);
if (wrid.value!=0)
@ -138,7 +136,7 @@
function delRow() {
$.ajax({
url: "<%=ViewData["WebApiUrl"]+"/DropWritting"%>",
url: "<%=ViewData["WABASEWF"]+"/DropWritting"%>",
type: "Get",
data: { wrid: wrid.value },
contentType: 'application/json; charset=utf-8',
@ -153,25 +151,27 @@
wredit(0);
},
error: function (xhr, ajaxOptions, thrownError) {
$("#msg").text(xhr.status+" : "+xhr.responseText);}
message(xhr.status+" : "+xhr.responseText);}
});
}
function setRow() {
var wrt = GetWritting();
$.ajax({
url: "<%=ViewData["WebApiUrl"]+"/UpdateWritting"%>",
type: "POST",
data: JSON.stringify(wrt),
contentType: 'application/json; charset=utf-8',
success: function () {
url: "<%=ViewData["WABASEWF"]+"/UpdateWritting"%>",
type: 'POST',
data: wrt,
success: function (ms) {
if (ms)
message(JSON.stringify(ms));
else {
var cells = document.getElementById("wr"+wrt.Id).getElementsByTagName("TD");
cells[0].innerHTML=wrt.Description;
cells[1].innerHTML=wrt.ProductReference;
cells[2].innerHTML=wrt.UnitaryCost;
cells[3].innerHTML=wrt.Count;
}
},
error: function (xhr, ajaxOptions, thrownError) {
$("#msg").text(xhr.status+" : "+xhr.responseText);}
@ -183,20 +183,27 @@ function addRow(){
var wrt = GetWritting();
var estid = parseInt($("#Id").val());
$.ajax({
url: "<%=ViewData["WebApiUrl"]+"/Write"%>/?estid="+estid,
url: "<%=ViewData["WABASEWF"]+"/Write"%>/?estid="+estid,
type: "POST",
traditional: false,
data: JSON.stringify(wrt),
contentType: 'application/json; charset=utf-8',
data: wrt,
success: function (data) {
wrt.Id = Number(data);
wredit(wrt.Id);
$("<tr class=\"row\" id=\"wr"+wrt.Id+"\"><td>"+wrt.Description+"</td><td>"+wrt.ProductReference+"</td><td>"+wrt.Count+"</td><td>"+wrt.UnitaryCost+"</td></tr>").appendTo("#wrts");
wrid.value = wrt.Id;
var wridval = 'wr'+wrt.Id;
//$("#wrts").append("<tr class=\"selected row\" id=\"wr"+wrt.Id+"\"><td>"+wrt.Description+"</td><td>"+wrt.ProductReference+"</td><td>"+wrt.Count+"</td><td>"+wrt.UnitaryCost+"</td></tr>");
jQuery('<tr/>', {
id: wridval,
"class": 'selected row',
}).appendTo('#wrts');
$("<td>"+wrt.Description+"</td>").appendTo("#"+wridval);
$("<td>"+wrt.ProductReference+"</td>").appendTo("#"+wridval);
$("<td>"+wrt.Count+"</td>").appendTo("#"+wridval);
$("<td>"+wrt.UnitaryCost+"</td>").appendTo("#"+wridval);
$("#"+wridval).click(function(ev){onEditRow(ev);});
},
error: function (xhr, ajaxOptions, thrownError) {
$("#msg").text(xhr.status+" : "+xhr.responseText);}
});
$(".wr"+wrt.Id).click(onEditRow(e));
message(xhr.status+" : "+xhr.responseText);}});
}
function ShowDtl(val)
@ -211,7 +218,7 @@ function addRow(){
var hid=e.delegateTarget.id;
var vwrid = Number(hid.substr(2));
wredit(vwrid);
$("#Description").val(cells[0].innerHTML);
$("#wrdesc").val(cells[0].innerHTML);
$("#ProductReference").val(cells[1].innerHTML);
$("#Count").val(cells[2].innerHTML);
$("#UnitaryCost").val(Number(cells[3].innerHTML.replace(",",".")));
@ -227,7 +234,7 @@ function addRow(){
$(".row").click(function (e) {onEditRow(e);});
$("#btnnew").click(function () {
wredit(0);
$("#Description").val("");
$("#wrdesc").val("");
$("#ProductReference").val("");
$("#Count").val(1);
$("#UnitaryCost").val(0);
@ -236,6 +243,8 @@ function addRow(){
</script>
</div>
</form>
<a class="actionlink" href="<%=ViewData["WebApiBase"]%>/FrontOffice/GetEstimTex?estimid=<%=Model.Id%>"><%= LocalizedText.Tex_version %></a>
<a class="actionlink" href="<%=ViewData["WebApiBase"]%>/FrontOffice/GetEstimPdf?estimid=<%=Model.Id%>"><%= LocalizedText.Pdf_version %></a>
</asp:Content>

@ -1,167 +0,0 @@
\documentclass[french,11pt]{article}
\usepackage{babel}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[a4paper]{geometry}
\usepackage{units}
\usepackage{bera}
\usepackage{graphicx}
\usepackage{fancyhdr}
\usepackage{fp}
\def\TVA{20} % Taux de la TVA
\def\TotalHT{0}
\def\TotalTVA{0}
\newcommand{\AjouterService}[2]{% Arguments : Désignation, prix
\FPround{\montant}{#2}{2}
\FPadd{\TotalHT}{\TotalHT}{\montant}
\eaddto\ListeProduits{#1 & \montant \cr}
}
\newcommand{\AfficheResultat}{%
\ListeProduits
\FPeval{\TotalTVA}{\TotalHT * \TVA / 100}
\FPadd{\TotalTTC}{\TotalHT}{\TotalTVA}
\FPround{\TotalHT}{\TotalHT}{2}
\FPround{\TotalTVA}{\TotalTVA}{2}
\FPround{\TotalTTC}{\TotalTTC}{2}
\global\let\TotalHT\TotalHT
\global\let\TotalTVA\TotalTVA
\global\let\TotalTTC\TotalTTC
\cr
\hline
\textbf{Total} & & & \TotalHT
}
\newcommand*\eaddto[2]{% version développée de \addto
\edef\tmp{#2}%
\expandafter\addto
\expandafter#1%
\expandafter{\tmp}%
}
\newcommand{\ListeProduits}{}
%%%%%%%%%%%%%%%%%%%%% A MODIFIER DANS LA FACTURE %%%%%%%%%%%%%%%%%%%%%
\def\FactureNum {<none>} % Numéro de facture
\def\FactureAcquittee {non} % Facture acquittée : oui/non
\def\FactureLieu {Suresnes} % Lieu de l'édition de la facture
\def\FactureObjet {Facture} % Objet du document
% Description de la facture
\def\FactureDescr {%
Cette facture concerne la prestation de coiffure a domicile du Mardi 23 Septembre 2014 à Meudon, 6 rue de la Verrerie.
}
% Infos Client
\def\ClientNom{M. Dupont} % Nom du client
\def\ClientAdresse{% % Adresse du client
Zenosphere : Anthony Courtois (DG)\\
6 rue de la Verrerie\\
92190 MEUDON\\
Mobile: 06 47 60 25 50
}
% Liste des produits facturés : Désignation, prix
\AjouterService {Forfait Coiffure} {75}
%\AjouterService {Frais de déplacement} {0.84}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\geometry{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex}
\thispagestyle{fancy}
\pagestyle{fancy}
\setlength{\parindent}{0pt}
\renewcommand{\headrulewidth}{0pt}
\cfoot{
Soraya Coiffure - 2 boulevard Aristide Briand - 92150 SURESNES \newline
\small{
E-mail: soraya.boudjouraf@free.fr\\
Téléphone mobile: +33(0)6 37 57 42 74\\
Téléphone fixe: +33(0)9 80 90 36 42
}
}
\begin{document}
% Logo de la société
%\includegraphics{logo.jpg}
% Nom et adresse de la société
Soraya Schneider\\
2 boulevard Aristide Briand\\
Bat V\\
92150 SURESNES\\
Facture n°\FactureNum
{\addtolength{\leftskip}{10.5cm} %in ERT
\textbf{\ClientNom} \\
\ClientAdresse \\
} %in ERT
\hspace*{10.5cm}
\FactureLieu, le \today
~\\~\\
\textbf{Objet : \FactureObjet \\}
\textnormal{\FactureDescr}
~\\
\begin{center}
\begin{tabular}{lrrr}
\textbf{Désignation ~~~~~~} & \textbf{Montant (EUR)} \\
\hline
\AfficheResultat{}
\end{tabular}
\end{center}
\begin{flushright}
\textit{Auto entreprise en franchise de TVA}\\
\end{flushright}
~\\
\ifthenelse{\equal{\FactureAcquittee}{oui}}{
Facture acquittée.
}{
À régler par chèque ou par virement bancaire :
\begin{center}
\begin{tabular}{|c c c c|}
\hline \textbf{Code banque} & \textbf{Code guichet} & \textbf{N° de Compte} & \textbf{Clé RIB} \\
20041 & 00001 & 1225647F020 & 05 \\
\hline \textbf{IBAN N°} & \multicolumn{3}{|l|}{ FR 91 20041 00001 1225647F020 05 } \\
\hline \textbf{Code BIC} & \multicolumn{3}{|l|}{ PSSTFRPPPAR } \\
\hline
\end{tabular}
\end{center}
}
\end{document}

@ -30,10 +30,13 @@
\def\TotalTVA{0}
\newcommand{\AjouterService}[2]{% Arguments : Désignation, prix
\FPround{\montant}{#2}{2}
\newcommand{\AjouterService}[3]{% Arguments : Désignation, quantité, prix
\FPround{\prix}{#3}{2}
\FPeval{\montant}{#2 * #3}
\FPround{\montant}{\montant}{2}
\FPadd{\TotalHT}{\TotalHT}{\montant}
\eaddto\ListeProduits{#1 & \montant \cr}
\eaddto\ListeProduits{#1 & \prix & #2 & \montant \cr}
}
@ -96,7 +99,7 @@
<# foreach (Writting wr in estim.Lines) { #>
\AjouterService {<#=wr.Description#> <# if (!string.IsNullOrWhiteSpace(wr.ProductReference)) { #>
(<#=wr.ProductReference#>)<# } #>} {<#=wr.UnitaryCost*wr.Count#>}
(<#=wr.ProductReference#>)<# } #>} {<#=wr.Count#>} {<#=wr.UnitaryCost#>}
<# } #>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -155,7 +158,7 @@ Facture n°\FactureNum
\begin{center}
\begin{tabular}{lrrr}
\textbf{Désignation ~~~~~~} & \textbf{Montant (EUR)} \\
\textbf{Désignation ~~~~~~} & \textbf{Prix unitaire} & \textbf{Quantité} & \textbf{Montant (EUR)} \\
\hline
\AfficheResultat{}
\end{tabular}

@ -6,7 +6,8 @@ namespace Yavsc.Model.RolesAndMembers
{
public class RegisterViewModel
{
[DisplayName("Nom d'utilisateur")]
[Localizable(true)]
[Display(Name="User name")]
[Required(ErrorMessage = "S'il vous plait, entrez un nom d'utilisateur")]
public string UserName { get; set; }

@ -20,26 +20,34 @@ namespace Yavsc.Model.WorkFlow
/// Who knows?
/// </summary>
/// <value>The unitary cost.</value>
[Required()]
[Display(Name="Coût unitaire")]
[Required(ErrorMessage="Veuillez renseigner un coût unitaire")]
public decimal UnitaryCost { get; set; }
/// <summary>
/// Gets or sets the count.
/// </summary>
/// <value>The count.</value>
[Required()]
[Required(ErrorMessage="Veuillez renseigner un multiplicateur pour cette imputation")]
public int Count { get; set; }
/// <summary>
/// Gets or sets the product reference.
/// </summary>
/// <value>The product reference.</value>
[Required()]
[Required(ErrorMessage="Veuillez renseigner une référence produit")]
[StringLength(512)]
public string ProductReference { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
[Required()]
[Required(ErrorMessage="Veuillez renseigner une description de cette imputation.")]
[StringLength (2048)]
public string Description { get; set; }
public override string ToString ()
{
return string.Format ("[Writting: Id={0}, UnitaryCost={1}, Count={2}, ProductReference={3}, Description={4}]", Id, UnitaryCost, Count, ProductReference, Description);
}
}
}

Loading…